7 ms·
Lifetimes can be confusing as hell, especially declaring them correctly. It looks like section 10.3 in this new book improves things.
by cgh 10y ago
Lifetimes can be confusing as hell, especially declaring them correctly. It looks like section 10.3 in this new book improves things.
- steveklabnik 10y agoAnd to be clear, 10.3 is just the intro; we have a full chapter later with the complicated stuff. We decided to do it this way so that you can write real Rust code without going "what is that syntax"; you don't need to have a full understanding of the more exotic aspects of lifetimes to write useful code in Rust.
- Ygg2 10y agoHere is something, that might ease things: https://rufflewind.com/2017-02-15/rust-move-copy-borrow https://rufflewind.com/2017-02-15/rust-move-copy-borrow
- vvanders 10y agoOkay, that's really cool and does probably the best job I've seen of an overview of &mut and &. I feel like you could almost build a rust cheat-sheet poster out of this and few other useful syntax visualizations(traits, generic + lifetime syntax, etc).
- kbenson 10y agoReading that, and the little blurb about move and copy semantics below the infographic, it occurred to me that borrowing is actually rather easily described as real like sharing (which is unfortunately already a loaded term in much of CS due to multiprocessing). When you share something that is not copyable (such as a pen), you give it to the person borrowing it until they return it when done. When you share something that is copyable (and idea, some software, a joke), you give someone their own copy to do with as they please. There is no return required. Borrowing, which is from the receiver's perspective, implies returning, while sharing, from the sender's perspective, may or not mat imply a later return of something. I wonder how much the conceptual baggage of the terminology affects how people learn them.
- cpeterso 10y agoAs a C++ programmer, I understand ownership and borrowing, but I find the lifetime syntax impenetrable. It clutters the code with annotations that seem like they could be deduced by the compiler. For example, a variable's lifetime is relative to another variable or function. Why must we create a placeholder name like 'a instead of referring to the other variable by name? Take this example from section 10.3: fn longest<'a>(x: &'a str, y: &str) -> &'a str { x } Why couldn't we write it as something like the following? fn longest(x: &str, y: &str) -> &'x str { x } Or instead of requiring lifetimes everywhere, the compiler could use conservative defaults but allow optional lifetime annotations to reduce the lifetime if needed or to release memory sooner. For example, in this struct from section 10.3, why is 'a necessary when config is a non-mut reference and thus must outlive a struct App instance? There is a Rust issue filed for this: https://github.com/rust-lang/rfcs/issues/1532 https://github.com/rust-lang/rfcs/issues/1532 struct App<'a> { name: String, config: &'a Config, }
- Manishearth 10y agoThe Rust community prefers explicitness in many cases. We already have lifetime elision for simpler things. Both of these cases in your comment have been extensively discussed and decided against. The reason why the former is not done is because it makes the signature dependent on the body. This means that changing some code could potentially change the signature of the function, which is the kind of silent magic that folks would prefer to be explicit. Also, as the function gets larger, this is harder to deduce by looking at the code. The current elision rules all operate on the signature, so you only need to look at the signature to figure out the lifetimes, elision or not. The latter is way more controversial. The reason there is similar; folks want it to be obvious from the type name that it is a borrowing type, and changes to the internals shouldn't magically change the signature without an explicit acknowledgement. Even type arg lifetime elision was controversial. Currently, in Rust, you can write a function like `fn foo(x: &u8) -> Foo`, where `Foo` is actually `Foo<'a>`, and the compiler inserts the lifetimes in the right places. When this was proposed there was a good chance that it would not happen (though as you can see it did happen). So the status quo on lifetime elision is probably not going to change in my opinion, given how hard it was to make the last one happen (of course, you have the "overton window" of acceptable implicitness gradually shifting due to that change, so it might after all) To me personally, this explicitness is a minor annoyance when dealing with simpler stuff, but is invaluable in codebases making heavier use of lifetimes, like rustc (which avoids reference counting, even though compilers generally need a lot of tricky sharing). And ultimately, I appreciate it even in codebases with fewer lifetimes; I don't like having to peek at a function's code to understand how it's supposed to be used.