TMI: Exhausting all the possibilities
Aug. 8th, 2012 06:31 pmI closed #2735, which had to do with how
and
can have different semantics in Rust. As you might guess, "_" effectively means "I'm never going to use this, so just give it a name I can't refer to." (Left-hand sides of let decls, by the way, can be any irrefutable pattern.) So why would they be different?
e might have a struct type (structs are the new classes) that has a destructor. So in the first case, the code generator looks at the decl and says "we're binding e to something, so run the destructor when that something goes out of scope". So if you actually had:
e's destructor would run, presumably having observable effects, after "Hello" got printed out. On the other hand, with just e; the code generator sees that you're not doing anything with the result of e;, and runs the destructor immediately after evaluating e.
I changed it by adding a special case that checks the pattern on the left-hand side for "_"-ness. It feels a little odd to have this special case, but weirder to have these two forms behave differently.
Mainly, though, I'm still working on removing non-exhaustive matches from the Rust codebase. I started compiling a list of all the non-exhaustive matches I removed, and classifying them roughly. The hope is that this will be evidence that we can actually use to figure what, if any, language features to add to make it easier and safer to express the knowledge that's in your head (but not communicated to the compiler) when you wrote a match check before. (match is the new alt. Oh, syntax changes.)
let _ = e;
and
e;
can have different semantics in Rust. As you might guess, "_" effectively means "I'm never going to use this, so just give it a name I can't refer to." (Left-hand sides of let decls, by the way, can be any irrefutable pattern.) So why would they be different?
e might have a struct type (structs are the new classes) that has a destructor. So in the first case, the code generator looks at the decl and says "we're binding e to something, so run the destructor when that something goes out of scope". So if you actually had:
let _ = e;
error!("Hello");
e's destructor would run, presumably having observable effects, after "Hello" got printed out. On the other hand, with just e; the code generator sees that you're not doing anything with the result of e;, and runs the destructor immediately after evaluating e.
I changed it by adding a special case that checks the pattern on the left-hand side for "_"-ness. It feels a little odd to have this special case, but weirder to have these two forms behave differently.
Mainly, though, I'm still working on removing non-exhaustive matches from the Rust codebase. I started compiling a list of all the non-exhaustive matches I removed, and classifying them roughly. The hope is that this will be evidence that we can actually use to figure what, if any, language features to add to make it easier and safer to express the knowledge that's in your head (but not communicated to the compiler) when you wrote a match check before. (match is the new alt. Oh, syntax changes.)
(no subject)
Date: 2012-08-16 07:18 am (UTC)(no subject)
Date: 2012-08-16 11:09 pm (UTC)let (a, (_, b), _) = ...;
though. (Supposing that the wildcarded things both have destructors.) Which I know I didn't mention in my OP, but special-casing let _ = ... logically leads to the question of how to handle things with nested _s.
(no subject)
Date: 2012-08-17 09:23 pm (UTC)- evaluate the right-hand side,
- bind its .1 component to a and its .2.2 component to b (this isn't something that can somehow have side-effects, is it?), and then
- run the destructors on the .2.1 and .3 components (in that order if destructors otherwise fire queue style, in the opposite order if they fire stack style), because these parts were "bound" to _, which gives them a trivial scope by virtue of _ being the programmer's declaration that they don't care about whatever this would be bound to.
You are going to go against someone's intuition no matter how you do this. Functional programmers would expect cases like the one you first posted to work the same way, while C++ programmers aren't used to _ being special and would expect it to behave exactly like any other variable. I obviously have my own bias in siding with the functional programmers, but if that's the way you go with Rust, I think that declaring, in the part of the language definition that talks about _, that if effectively has a trivial, immediately-closed scope is a sensible way to do it. (But, again, I don't know Rust like you do, so maybe there are still ambiguities I'm not seeing.)(no subject)
Date: 2012-08-18 09:53 pm (UTC)