3 ms·
I disagree. :) I have experience in low level programming and several MLs (and lots of misc other languages), so I don't think my opinions are shaped solely by
by weberc2 9y ago
I disagree. :) I have experience in low level programming and several MLs (and lots of misc other languages), so I don't think my opinions are shaped solely by familiarity.
There is a lot to like about Rust, but here are some things that I think make rust hard (and I think the difficulty of these features outweigh any productivity granted by the safety features).
First, the affine type system--I don't understand the use case for pushing ownership deeper into the stack by default, and it seems crazy that I have to know how all instances of a given type will be passed at definition time. It doesn't seem to facilitate safety, or at least it seems like the most flat-footed approach to preventing double-frees (never let two functions access the same memory, even those that can be trivially proven to run sequentially). I literally never want to move--I always want to shallow copy (or reference). Further, you can't call most pass-by-value functions from inside a pass by reference function because the former usually wants to destroy the value. Copy works around this in the simplest cases, but for the most part you end up with two categories of classes that can't be mixed. This is especially bad for open traits, where the trait definition has to decide how all implementations will pass and receive their arguments.
Beyond that, when defining a struct type that holds a reference, you have to know at definition time the lifetime of that reference, even it may vary from instance to instance (sometimes I want the struct to own the reference, but sometimes I don't). Maybe there's a way to express this in Rust, but it seems like the default is to assume that lifetimes are part of the type. This creates another dichotomy in types: those that own their data and references to data (String vs STR, vec vs slice, etc).
I like Rust. I love that it's an ML with a sane syntax, or a C-family language with decent functional features and sum types. I just can't justify these impediments. :(
- wtetzner 9y ago> it seems like the most flat-footed approach to preventing double-frees (never let two functions access the same memory, even those that can be trivially proven to run sequentially) This might just be a case of needing non-lexical lifetimes. I'm not sure about that, because I don't know the details of what issues you ran into. > Beyond that, when defining a struct type that holds a reference, you have to know at definition time the lifetime of that reference, even it may vary from instance to instance (sometimes I want the struct to own the reference, but sometimes I don't). The main issue I run into is when I want a struct to own a value, and for that struct to also include a reference into that value.
- weberc2 9y ago> This might just be a case of needing non-lexical lifetimes. I'm not sure about that, because I don't know the details of what issues you ran into. I don't think the issue is about lexical lifetimes, but I don't have a very good understanding of what that means, either. My issue is that I frequently want to take some data and pass it into two separate functions, but I can't do this unless those functions are designed to take the value by reference. Even if those values contain owned references into the heap, the programs can be trivially proven to be correct. I can work around this by always passing references, but it seems strange to use reference semantics when all I really want is to not destroy the original value. Another common case is that I have a borrowed reference that points to data I would like to pass (by value) into another function: `fn foo(bar: &Bar) { baz(bar.x) }`.
- wtetzner 9y ago> Even if those values contain owned references into the heap, the programs can be trivially proven to be correct. I don't think that's true. Passing data to a function when it's not a reference means that you're giving ownership to that function. How could you then pass it to another function (unless the first returned it again)? Once a function has ownership, it means nothing else has ownership of the data. Basically, if you write a function that just takes a value, and doesn't take a reference, then you're telling your callers to give you ownership. If you don't want to take ownership, then just always accept a reference. Maybe I'm misunderstanding the problem you're having. Do you have an example of another language that lets you do what you want?
- weberc2 9y ago> Passing data to a function when it's not a reference means that you're giving ownership to that function. Only in Rust, but there's no apparent reason to move ownership. If Rust didn't move ownership during pass-by-value, it would still be trivial to prove the following code correct: fn main() { let x = String::new(); foo(x); bar(x); // x is destroyed } > Maybe I'm misunderstanding the problem you're having. Do you have an example of another language that lets you do what you want? Here's the equivalent in C++: int main() { std::string x = ""; foo(x); bar(x); // x is destroyed }