> Rust's compiler prevents you from moving data into a method which then nulls it out
Just for clarity, we have this in Nim too, eg:
type
Foo = ref object
Bar = object
val: Foo not nil
let
f = Bar() # Error, 'val' must be set
proc foobar(f: Foo not nil): Foo not nil =
return nil # Error, can't return nil
foobar(nil) # Error, can't pass nil
> At best this dangling pointer will look at garbage and cause a crash or undefined behavior. At worst, it will look at other, actively-used memory and cause a security vulnerability.
In C/C++, yes, but this isn't so applicable to Nim where we have GCed references and 'not nil' constraints.
> If you use iterators in Rust, you never need to worry about out of bounds errors
Well I was not talking about iterating through a list, but rather maintaining arbitrary indexes to a mutable list. Eg, a Sprite which contains a index to a Texture array. In that scenario it's just as easy to miscalculate and crash your program via a bounds-checking error as it is to crash by nil-deref.
> To offer a counter-viewpoint, I find that Option<> (and Result<>) are very easy to reason about..
It's good that Rust works for you, truly. And like I said in another post, I agree Rust's design here may be better for some domains. However, Nim's design still feels more elegant and straight-forward to me. Luckily, we both get a powerful language that suits us, regardless of which one we prefer :)
Oh, sorry, I should have been clearer: I wasn't trying to disparage Nim at all (it's on my list of languages to play with). I was just clearing up some points about Rust :)
Edit:
> Well I was not talking about iterating through a list, but rather maintaining arbitrary indexes to a mutable list. Eg, a Sprite which contains a index to a Texture array. In that scenario it's just as easy to miscalculate and crash your program via a bounds-checking error as it is to crash by nil-deref.
The solution here is to just use a reference instead of an arbitrary index. If you hand out references you can lean on the compiler to enforce memory safety -- the compiler won't let you access data that is no longer alive, won't let you accidentally share across thread boundaries if you don't explicitly want that, etc. And if that was a shared mutable list, it's doubly important to let the compiler help you reason about it, since shared, mutable state is the main source of data races.
This is one of those cases where leveraging the compiler allows you to write better, safer code.
No worries. I also wasn't implying you where trying to discourage Nim, and I hope my post didn't come off as accusatory. Cheers!
EDIT:
> The solution here is to just use a reference instead of an arbitrary index.
Ah, sorry I should have said "mutable Texture array". In that case Rust's borrow-checking will 'freeze' the array, preventing Textures from ever being changed during the lifetime of your Sprites. So you're left with either Option<> or indexing as a solution, each with it's own merits, but neither as.. practical as nilable GCed references, IMO (again, just my opinion.. others seem to find it easy enough).
Out of curiosity: How do you convert from a nilable `Foo` to a `Foo not nil`? As in, what do you do if you have a function maybe returning a `Foo` and want to pass its value to a function taking `Foo not nil`?
You prove to the compiler that the nilable var is not-nil via if statement. Eg:
proc foobar(f:Foo not nil) =
discard
let f = Foo() # nilable ref
let b: Foo not nil = f # Error, can't prove 'f' is not nil
if f != nil:
let b: Foo not nil = f # can assign non-nil vars to 'f'
foobar(f) # or can pass 'f' directly
Just for clarity, we have this in Nim too, eg:
> At best this dangling pointer will look at garbage and cause a crash or undefined behavior. At worst, it will look at other, actively-used memory and cause a security vulnerability.In C/C++, yes, but this isn't so applicable to Nim where we have GCed references and 'not nil' constraints.
> If you use iterators in Rust, you never need to worry about out of bounds errors
Well I was not talking about iterating through a list, but rather maintaining arbitrary indexes to a mutable list. Eg, a Sprite which contains a index to a Texture array. In that scenario it's just as easy to miscalculate and crash your program via a bounds-checking error as it is to crash by nil-deref.
> To offer a counter-viewpoint, I find that Option<> (and Result<>) are very easy to reason about..
It's good that Rust works for you, truly. And like I said in another post, I agree Rust's design here may be better for some domains. However, Nim's design still feels more elegant and straight-forward to me. Luckily, we both get a powerful language that suits us, regardless of which one we prefer :)