Underscore is not exactly "throw away" but rather it is exactly "don't bind to variable". The difference becomes evident with the statement: let _ = x; This is a no-op and the value remains owned by the variable x, and is not thrown away.
Underscore can also be the equivalent of a wildcard in pattern matching.
A half-hour to learn Rust
91–100 of 150 posts
Re: A half-hour to learn Rust
#92Has anyone struggled with the lack of HashMap literal? https://github.com/rust-lang/rfcs/issues/542 I just cant bring myself to doing something like this: let m1: HashMap = [("Sun", 10), ("Mon", 11)]. iter(). cloned(). collect(); https://doc.rust-lang.org/std/collections/struct.HashMap.htm...
I assume it must be a common finding. I have too wanted to initialize a map value with a literal, only to find that one needs to juggle around their absence. Very weird thing to miss in such a great language, really. I guess that the syntax proposal would get too complicated with all the different possibilities (like what happens if you want to store one ref, now you need to add lifetimes and such), but overall for t…
Re: A half-hour to learn Rust
#93Has anyone struggled with the lack of HashMap literal? https://github.com/rust-lang/rfcs/issues/542 I just cant bring myself to doing something like this: let m1: HashMap = [("Sun", 10), ("Mon", 11)]. iter(). cloned(). collect(); https://doc.rust-lang.org/std/collections/struct.HashMap.htm...
Would a macro work ok for you, like the inbuilt vec! one? There's a crate for that named maplit: https://docs.rs/maplit let map = hashmap!{ "a" => 1, "b" => 2, };
Re: A half-hour to learn Rust
#94Earlier quoted context omitted.
But that's just not true. There is a single principled way for C declarations, and it's as easy as "a binding is a type name followed by an expression and a semicolon". And while small inconcistencies have been introduced over time, it was entirely consistent when C was conceived. The problem is just that the simple rule how to read declarations is not well-known (I don't understand why). See my other comments.
> "a binding is a type name followed by an expression and a semicolon". I find this hard to grock. Do you have a link to the actual grammar and production rules that apply to all cases ? When I see: int a = 3; I don't see a "type name followed by an expression and a semicolon", but rather the grammar "TYPE_NAME NAME = EXPR;". However, that's not correct, since TYPE_NAME cannot be any type (e.g. a function pointer won…
A google search returns that Annex A in the ISO standard has something like a grammar. Here is a link to an unoffical version of the standard: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf . However, this is not the right place to look for if you just want to understand the simple principle behind declarations, because the purity of declaration syntax has been considerably diluted in the last decades. So, only look in the standard if you are in good psychological health, and need to implement a production-grade C compiler. Also, note that grammars are overrated and tend to make things more complex than they really are. They are often too theoretical of a construct to be applicable, and that is certainly true for a language like C.
Instead, I recommend you to read the idea from the horse's mouth, here is Dennis Ritchie talking about it: https://www.bell-labs.com/usr/dmr/www/chist.html
> I don't see a "type name followed by an expression and a semicolon", but rather the grammar "TYPE_NAME NAME = EXPR;".
Let's ignore the optional equal sign + initializer expression, and just focus on declarations without initializers. The syntax is (as I said) "TYPE_NAME EXPR;" where EXPR is an expression that makes use of the newly-declared variable.
> (e.g. a function pointer won't work)
Not sure what you mean by "function pointer", but I'm pretty sure it's not a type name in the way I mean it. Here is how to look at your examples:
void (*foo)(int) = expr;
^^^^^^ (optional) initializer
^^^^^^^^^^^ expression (originally it was (*foo)(x),
but as I said nowadays there are
type specifiers in the list which
is a little inconsistent.)
^^^^ type name
int (*foo)[N] = expr;
^^^^^^ (optional) initializer
^^^^^^^^ expression
^^^ type name
Basically, the first example says "(* foo)(int) is a void", so you can conclude that foo is a pointer to a function that takes an int and returns a void.The second example says "(* foo)[N] is an int" so you can conclude that foo is a pointer to an array of N ints.
Re: A half-hour to learn Rust
#95Earlier quoted context omitted.
I assume it must be a common finding. I have too wanted to initialize a map value with a literal, only to find that one needs to juggle around their absence. Very weird thing to miss in such a great language, really. I guess that the syntax proposal would get too complicated with all the different possibilities (like what happens if you want to store one ref, now you need to add lifetimes and such), but overall for t…
you can so easily make a macro in rust to do exactly what you want, that it doesn't likely make sense to include it as part of the language. though perhaps the macro should be in the core libs.
Also writing macros is not the first thing one learns when getting to know Rust, so a learner won't know how to write her own.
Re: A half-hour to learn Rust
#96Earlier quoted context omitted.
I debated which terminology to use and thought "throwing away" was more intuitive, especially if you've never heard of "binding" before. It has its limits though - in your example I'd say you're throwing away the result of evaluating "x", just as if you did: x;
I don't agree, because we can try the two following programs, and one of them does not compile. It compiles with underscore. 1: let x = String::new(); let _ = x; println!("{}", x); 2: let x = String::new(); x; println!("{}", x); // ERROR: Use of moved value x. And this is why I said that let _ = x; is a no-op. :)
let _ = timeConsumingFunction();
Still needs to spend time performing timeConsumingFunction(). It might not move, but it still did the work and produced a result, you are just not using it (and hence not need to move it).If you you had lazy evaluation I would agree with your argument for the no-op terminology.
Re: A half-hour to learn Rust
#97This is the most useful introduction to a language I have ever read. Often language introductions produce a "Wall of Complexity" and I failed in my last two attempts learning Rust failed because of that. This is just great. Be warned, the "half hour" part is probably a bit like "99 cents" as a price tag. I've already spent more than that, but it is time well spent. Thanks for writing this!
https://pandas.pydata.org/pandas-docs/stable/getting_started...
Re: A half-hour to learn Rust
#98Earlier quoted context omitted.
I don't agree, because we can try the two following programs, and one of them does not compile. It compiles with underscore. 1: let x = String::new(); let _ = x; println!("{}", x); 2: let x = String::new(); x; println!("{}", x); // ERROR: Use of moved value x. And this is why I said that let _ = x; is a no-op. :)
Huh, TIL! Had no idea `x;` actually moved `x`.
Re: A half-hour to learn Rust
#99Rust in general is just such a pleasant language. It can be a bit tedious to work with sometimes, but that's usually because the problem in question is tedious in and of itself, and you didn't even notice all of the little screw-ups that could occur until you saw how Rust dealt with it.
Re: A half-hour to learn Rust
#100Earlier quoted context omitted.
I don't agree, because we can try the two following programs, and one of them does not compile. It compiles with underscore. 1: let x = String::new(); let _ = x; println!("{}", x); 2: let x = String::new(); x; println!("{}", x); // ERROR: Use of moved value x. And this is why I said that let _ = x; is a no-op. :)
I think no-op is a bit misleading, and throwing away is more precise, since Rust has strict evaluation. let _ = timeConsumingFunction(); Still needs to spend time performing timeConsumingFunction(). It might not move, but it still did the work and produced a result, you are just not using it (and hence not need to move it). If you you had lazy evaluation I would agree with your argument for the no-op terminology.