Live data from Hacker News

A half-hour to learn Rust

fasterthanli.me

131–140 of 150 posts

Re: A half-hour to learn Rust

#131

Earlier quoted context omitted.

The type signature of the function will also enforce that. You can also use generics to allow you to accept a wider variety of numbers, e.g.: fn takes_a_float_like_number (number: T) where T: Into { // Number will now be a 64-bit IEEE floating point object // for this scope let number = number.into(); } This will only compile if your type is small enough to fit into an f64. So takes_a_float_like_number(500_u32) will…

How would you improve that error?

"Literal {} is too large for type {}" (or thereabouts) is how this is handled in C/C++ compilers. I think it is enabled by -Wall or -Wpedantic in clang.

In other languages I've seen something like "type {} can't represent literal of value {}" which is a bit more generic and applies to things like floats/signed ints and values that are too small/negative.

Re: A half-hour to learn Rust

#132
post #87

Earlier quoted context omitted.

yes, it is dropped

Ahh, I remember a while back reading a blog where the author talked about `std::mem::drop` being their favorite standard library function because they used this effect. It is literately defined as: pub fn drop (_x: T) { } https://doc.rust-lang.org/std/mem/fn.drop.html

So is drop like free, or is it something else?

Re: A half-hour to learn Rust

#133

Earlier quoted context omitted.

Ahh, I remember a while back reading a blog where the author talked about `std::mem::drop` being their favorite standard library function because they used this effect. It is literately defined as: pub fn drop (_x: T) { } https://doc.rust-lang.org/std/mem/fn.drop.html

So is drop like free, or is it something else?

Yes, it's effectively like free -- though C++'s "auto" is probably a closer idea.

The main difference to C's free is that you simply cannot double-free a value (without using unsafe). That's because a value is only dropped when it falls out of scope -- and since it values only have one owner (and references must not outlive values) there cannot be any dangling references after it's dropped.

The documentation for std::mem::drop is explaining that dropping a value is a property of the language and scoping rules -- thus there is nothing for the function to do other than take a value and let it go out of scope.

Re: A half-hour to learn Rust

#135

I really enjoyed this, and it also inspired me to try a quick experiment: what would the same blog post look like for Go? I tried it and wrote https://dmitri.shuralyov.com/blog/27 . It was a fun exercise to go through.

Thanks for sharing. I enjoyed this. Could you elaborate on your closure example though? This was the only one I didn't understand. The explanation is a bit terse = "Closures are just variables with a function type with some captured context." Are these anonymous functions? What is calling the anonymous function if so in main? Cheers.

Thanks for feedback. I’ll try to clarify that section.

Re: A half-hour to learn Rust

#136

I really enjoyed this, and it also inspired me to try a quick experiment: what would the same blog post look like for Go? I tried it and wrote https://dmitri.shuralyov.com/blog/27 . It was a fun exercise to go through.

I did enjoy the article. It was quite clear but it made me wish for a bit more. Unlike the Rust article I didn't see any thing that suggested how golang might be special. I did see your suggestions for further reading though. I'll check them out.

Thanks for feedback. I think the “special” part for me was that there were large sections of the Rust blog that I could skip having to explain, because they aren’t a part of the Go language. To me, Go is a language that’s much easier to learn and be productive with. Maybe I should make it more clear what I skipped.

Re: A half-hour to learn Rust

#137

Earlier quoted context omitted.

One can make the argument that the C declaration syntax is simple, because the rule to make a declaration is to simply follow a type name by an expression where the declared variable is used. The fact that you write "int[4] arr" shows that you don't know how it works (which is not a criticism; it's just not well known how it works). The correct way is to write int arr[4]; and to interpret it as "arr[4] is an int" (wh…

> The correct way is to write > int arr[4]; > and to interpret it as "arr[4] is an int" No. The "interpretation" is "arr is an array of ints", and that is its type. The complexity of this declaration is evidenced by the fact that you spend another page of text "randomly" adding characters and delimiters around variable declarations to change its type : int arr[4] // array of ints int *arr[4] // array of pointers to i…

> The correct way is to write > int arr[4]; > and to interpret it as "arr[4] is an int" >> No. The "interpretation" is "arr is an array of ints", and that is its type.

I would have been happier if I had found my explanation interpreted in a more generous way. But that's basically what I was saying (and literally what I was saying in another comment).

As to the rest, the advantage of the C approach to type declarations is that there is no type declaration syntax. Just expression syntax. And that it's very terse.

> That's exactly why most languages said: "if we're changing the type, we're going to reflect this in the type". In a better world the examples above would be something like

There's a problem in that your proposed syntax is not even properly parseable. How would a parser recognize that your lines start with types i.e. are variable declarations? For example the example "* (int[]) arr", it would start reading the asterisk and the opening parenthese as an expression, and then suddenly find a type name (int), and could then not throw an error if it was one, but had to start all over again and try to parse the whole thing as a type declaration. That's not exactly nice - good syntax is parseable with a single token of lookahead. That not only makes parser implementations easier, but is also easier to read for humans and leads to better error detection.

Apart from that I think that your examples are about what D does, and this stuff is WORSE in my opinion. While the real problem with C declarations, which is the need to thread a symbol table through the lexer/parser, is still existent in D syntax (I believe), it introduces other problems:

How do you use an array that was declared as "int[5][10] arr"? Using it as "arr[4][9]" is an error: it must be "arr[9][4]". In other words, your approach to type declarations requires the programmer to constantly turn around declarations in his/her mind, which leads to lots of mistakes. It gets even harder when you add pointers / functions, for example "int* [5][10] arr" I believe you must access as "* arr[9][4]", or whatever the D dereference syntax is.

Java can afford to let you declare "int[][] arr = new int[5][10]" and let you access "arr[4][9]", at the cost of cheating. Java can "turn around" the dimensions because it doesn't actually have an "algebraic" type syntax, which it doesn't need because it doesn't have pointers / function pointers so there is no interaction there.

That's one of the reasons why most newer languages have the type to the right of the variable name, and types grow to the left (towards the variable name). For example, "let arr: [5][10]int" you can access as "arr[5][10]" which is easier, but that principled approach to syntactic construction of types also puts requirements on the expression syntax: For example, "let arr: [5][10]* int" would have to be accessed as "* arr[5][10]", which is weird - or the expression syntax must be changed to use a postfix dereference operator.

In short, it's not as easy as you thought, and the C syntax is in fact pretty smart. And from a practical standpoint I prefer the C way very much because it's so much terser and has less punctuation than all the alternatives. The only thing that annoys me is the lexer hack.

Re: A half-hour to learn Rust

#138
post #11

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.

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;

In C#, _ is called a "discard" variable. I found that to be an apt term to grasp the concept quickly.

Re: A half-hour to learn Rust

#139

Earlier quoted context omitted.

Ahh, I remember a while back reading a blog where the author talked about `std::mem::drop` being their favorite standard library function because they used this effect. It is literately defined as: pub fn drop (_x: T) { } https://doc.rust-lang.org/std/mem/fn.drop.html

So is drop like free, or is it something else?

It’s more like delete in c++ because you can implement the Drop trait for a type in order to run code when it is dropped in order to clean up resources etc.

Re: A half-hour to learn Rust

#140

Earlier quoted context omitted.

Ahh, I remember a while back reading a blog where the author talked about `std::mem::drop` being their favorite standard library function because they used this effect. It is literately defined as: pub fn drop (_x: T) { } https://doc.rust-lang.org/std/mem/fn.drop.html

So is drop like free, or is it something else?

Drop is just what runs when a value goes out of scope. Like destructors in C++.
Post reply on HN