8 ms·
I've often wondered why the extra scope is needed to borrow the same variable twice in a function. It seems like the borrow checker is being overly pedantic in
by weberc2 10y ago
I've often wondered why the extra scope is needed to borrow the same variable twice in a function. It seems like the borrow checker is being overly pedantic in this case by preventing users from writing a valid program. Rather than training new users to pacify the borrow checker, perhaps the borrow checker could be made to permit valid programs so as to reduce the learning curve?
- steveklabnik 10y agoThis is because the borrow checker is based on lexical scope at the moment. It can (and almost assuredly will) be based on non-lexical scope in the future, but enabling that has taken years of work to refactor the compiler in a way to let it happen. We're close, but not there yet. See also threads like this: https://internals.rust-lang.org/t/accepting-nested-method-calls-with-an-mut-self-receiver/4588 https://internals.rust-lang.org/t/accepting-nested-method-ca...
- xxr 10y agoCould you please elaborate a little on the difference here between lexical and non-lexical scope, Steve?
- Manishearth 10y ago{ let mut x = 1; let y = &x; do_stuff_with(y); x += 1 // not allowed right now because y is still active // no use of y here. possible uses of x } The lexical scope of y is the entire block (well, starting at the declaration of y). The actual scope of liveness ends after do_stuff_with -- y is no longer used at this point and x could be mutated. Right now you have to introduce an explicit scope block to make this work, but it's not a fundamental restriction in the language design.
- braveo 10y agoThe borrow checker drawbacks was actually one of my biggest pet peeves with Rust. If you guys can solve that problem in a meaningful way it'll go a long way towards making me a lot more positive about using Rust. As it was, I felt as though I were manipulating a child in order to get it to do what I wanted.
- Manishearth 10y agoFWIW a lot of the issues that newcomers come across are just due to lack of familiarity. The feeling of manipulating a child is common initially, but soon you'll learn how Rust expects you to design your code, and you'll be able to write code that passes the borrow checker without much issue. This is true when learning most languages; it takes some time to write code idiomatically, and till then you'll be stumbling over many things. But yeah, we should improve this.
- madez 10y agoThe learning curve of a program is relevant. Even if it just takes a weekend of fiddling around with Rust to learn how to satisfy it, it will hinder adoption.
- Manishearth 10y agoOf course. And we're always looking to improve that. Just pointing out that this isn't as major a problem when actually using Rust once you've picked it up.
- vvanders 10y agoI'd concur, in fact I don't know if I'd like to see the borrow checker get more complex because then it becomes harder to keep a mental model in your head of what it's doing. I found once I grasped the model that the borrow checker uses that I rarely fight it and instead find it catching many things that I miss.
- steveklabnik 10y agoYes, I've said this as well. Non-lexical lifetimes will make the borrow checker more complex; but if it tends to work closer to the way that you intuit things, as a developer, it will _feel_ simpler. Right now, the rules are very simple to explain. But that means your code gets a little more complex at times.
- vertex-four 10y agoThis is something that will be done as soon as MIR - a new internal layer in the Rust compiler which is easier to operate on - is stable. Right now, it's difficult to make the borrow checker much smarter and the work would have to be thrown away and reimplemented once MIR is stable.
- steveklabnik 10y agoMIR is already in the compiler and used for everything; it's not that MIR needs to become stable (it's unclear if it ever will be), the next step is porting borrowck to be a MIR pass rather than (I believe currently) a HIR pass.
- Lerc 10y agoIt certainly seems incongruous to require explicit scoping on usage when identifiers are reused inline. Conceptually they have the same principle where it shouldn't matter if you don't cross the streams.
- steveklabnik 10y agoThis is important for soundness; it's shadowing, not replacing let s = String::from("hello"); let s = &s[..]; Here, the second s refers to the first, and if it were go to out of scope right away, would be dangling. let s = String::from("hello"); { let s = &s[..3]; // here, s is "hell" } // here, it's the full string again This falls out of the usual scoping rules, basically. You can think of any variable introducing a new scope where it's valid; if you don't close it yourself, it's closed at the end of the enclosing scope.