Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'm surprised by the small warts in the Rust code. In this line:

  let fname = &*env::args().nth(1).unwrap_or("/usr/share/dict/words".into());
Neither the &* not the .into() add much to the meaning for me as a reader. Why are they necessary for the compiler?

Why is there an .unwrap() on writeln! ? To force a panic if the write failed?



They used `into()` because it's the shortest. The more idiomatic options are all of the other ones: String::from, .to_string(), and .to_owned() (Strings implement a lot of generic interfaces.)

The &* converts a String to a &str. I would have done it in the match rather than here, personally.

  > Why are they necessary for the compiler?
Rust does not heap allocate things automatically nor convert types automatically. This is the side effect: you can see exactly how much allocation is going on, and know exactly how stuff is being converted.

(We do have a very small and very specific set of conversions that will happen, they don't here, though.)

  > Why is there an .unwrap() on writeln! ? To force a panic if the write failed? 
Yes. The compiler will warn you that you're ignoring the errors on the write, and panicking is one way of handling it.


So the string literal is a str, but it gets turned into a String with the .into(), and then back into a str (well, &str) on the same line. That seems like a lot of travelling to go a short distance.

Is this because args() ultimately gives you a String, and you need to get that and the string literal out of a single expression?

Why does args() give you String rather than str? Couldn't command-line arguments be static more-or-less constants?

Would there be any way to turn the output of args straight into a &str, so you didn't need to .into() the literal?

Why are String and str separate types, rather than different uses of a single type? Are there any other examples of pairs of types with the same relationship? Arrays and slices? Any others? Is there something like this for maps/dictionaries/hashes?

I realise i've asked a lot of basic questions about strings here, and that you (and any other knowledgeable people reading this) are probably pretty tired of explaining this over and over again. I would be equally grateful for a link to some relevant documentation or whatever as for a detailed answer!


  > Is this because args() ultimately gives you a String
Correct.

  > Why does args() give you String rather than str?
Because the command-line arguments to the program are dynamically created. To be a &'static str, like a literal, they would have to be baked into the binary. &str is a pointer, it doesn't own any backing store.

  > Would there be any way to turn the output of args straight into a &str, so you didn't need to .into() the literal?
Well, so that is what this code is doing. It's just also handling error cases: What if the argument isn't passed in, as the primary one.

  > Why are String and str separate types, rather than different uses of a single type? 
Because String actually owns the data it's attached to, and is heap-allocated. &str is a pointer; it can only point at some other string type, since it doesn't store its own data.

  > Are there any other examples of pairs of types with the same relationship?
Arrays/Vectors and slices are a great example: String is wrapper around Vec<u8>, and &str one around &[u8]. But any owned/pointer variant with some sort of restriction on the type of data is going to be like this, it's a pretty common pattern.

Don't forget other string types too: There's OSString, CString, and external crates provide other types as well. Strings are not simple.

  > Is there something like this for maps/dictionaries/hashes?
Slices point to _contiguous_ spots in memory, so these data types don't really have slices, because that property isn't true.

  > I would be equally grateful for a link to some relevant documentation or whatever as for a detailed answer!
Hehe, no worries. It's hard to say what _specifically_ would be the best docs, but if you're interested in the _future_ docs about this, http://rust-lang.github.io/book/understanding-ownership.html is an explanation of Rust's ownership system through String and &str that will become the next version in the official docs. There's still some rough edges, as it's a draft, though!


If a panic is forced and used like an exception, that's just so wrong ...


> .into() > Why are they necessary ...

"foo" in Rust has the type &'static str. That is, a pointer and a length to an immutable piece of memory that never goes out of scope, which in the case of a literal is allocated statically. Because Rust tracks ownership of memory, this kind of a reference is of a different type than a heap-allocated owned string.

Rust also does no allocations or conversions without you telling it to do so. The programmer wanted a heap-allocated String, so he had to call a conversion function, which allocated memory and copied the string.


I'm not sure why the author kept &*s, I have suggested &s[..] instead (i.e. "take a slice of the whole string").

And .into() seems to be used as a &str -> String (allocating) conversion for brevity only.

The more idiomatic form would therefore be:

  let fname = &env::args().nth(1).unwrap_or(String::from("/usr/share/dict/words"))[..];
Although I would move the &...[..] slicing to the places where fname is used, which should work fine with &String instead of &str (or automatically coerce to get the latter).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: