28 comments

[ 3.2 ms ] story [ 56.0 ms ] thread
Seems standard enough, learning that a Vector is a resizable Array was needed, the auto closing sockets and files is cool. But what on Earth is string.into() ?

Not asking for an answer just pointing out that’s not a very useful name.

It would probably be more clear if the type was specified here instead of letting the compiler infer it.
If it's not clear that the compiler will infer the correct type, you can be specific. From the context, there's only one possible type though so that's redundant. Unlike C, older C++, or older Java, Rust expressions aren't necessarily fully typed in isolation. It's quite normal for a function to have an independently-generic return type and for the final type to be provided solely by context. Type inference only happens within named functions though: you need to specify your function signature.

Even if you find yourself wanting to know what the type actually is, the compiler (in its guise as a language server) can tell you that and let your IDE show you, rather than requiring you to type it out yourself.

Absolutely, I would have done the same in my code, I was more referring to the fact that sometimes it's helpful to be explicit when targeting a novice audience.
The only way I can figure out to make this explicit, though, seems very verbose and non-intuitive and frankly no clearer than `.into()` on its own:

Err(Into::<Box<dyn Error>>::into("sha1 hash is not valid"))

Is there a better way to constrain the trait implementation of Into?

You can (although I wouldn't recommend it) be explicit about the type using 'as':

    Err("sha1 hash is not valid".into()) as Result<(), Box<dyn Error>>
Or I'd often reach for an intermediate variable before a turbofish:

    let e: Box<dyn Error> = "".into();
    Err(e)
The 'as' trick is useful for helping the compiler to understand the return of a lambda where you need it to coerce errors to a specific type.
Are you referring to "sha1 hash is not valid".into()?

This is a "not very useful name" because it is a very generic (and core) trait. https://doc.rust-lang.org/std/convert/trait.Into.html This is the standard interface for converting anything into anything.

The way this is often used relies on type-inference. In this case it is being used as `return Err("sha1 hash is not valid".into())` inside a function that returns `Result<(), Box<dyn Error>>`. This means that the expression is inferred to evaluate to a `Box<dyn Error>`. Luckily there is an implementation of the Into trait for &str to that type (https://doc.rust-lang.org/std/convert/trait.From.html#impl-F...). So `.into()` just does the right thing.

This is a less common use case but the most common one I see is:

  let s: String = "foobar".into()
Which converts a &'static str to a String.
I am simply referring naming a method into(). Into… what? It’s like one of those 2010 frameworks that used .use(). The name communicates very little.

It sounds like it should be called .intoType() or .fixType() (it seems to use the compiler to work out the specific type) but again I don’t care about the answer I’m merely commenting that it’s not evident from reading the code as someone that knows a few languages.

But that's the thing. It is into anything. The method name can't be any more specific because the method isn't more specific.
Hence not mentioning a specific type in either suggestion. The method name can be understandable without having to resort to docs, and without having to mention specific types. With the current vague name I thought string.into() would be some kind of character converter.
“String” is not a useful name. How would a non-programmer guess what that means? It’s just another noun (like the verb “into”) that you have to learn.
Well yes, string isn’t a good name for people new to programming, but that’s a separate topic since most programming languages use the term. Python did similar with Arrays -> Lists and Hashmaps -> Dicts. into() isn’t a verb either.
> into() isn’t a verb either.

You got me there, smartypants. What my whole argument was hinging on.

I thought it was substantive (methods should be verbs and this isn’t one) but if you prefer please ignore that sentence and focus on the rest of my reply.
`into` is just for conversion of values, something which most programming languages have to deal with, just like most languages have strings. So it’s not like Rust has invented something which needs a new word belonging to <word group>.

So I agree. Or disagree with my original self.

Sure but I haven’t seen into() before in Perl, python, Ruby, C#, JS or TS. Whereas I have seen string.
the title is a bit misleading IMO; it doesn't generate a collision, it just generates a rainbow table and looks for matches on the fly.
Did the title change since you commented? I don't find the current version to be misleading - "cracking", although somewhat vague, does not imply collision.

(Title at time of writing this comment: "Learn Rust by implementing a SHA-1 hash cracker")

no, it's still the same. i had the impression, but i guess you're right. i did expect a collision attack and was slightly irritated when it turned out to be what it is.

that said, i'm not even correct, it's not a rainbow table but a dictionary attack. my bad!

It's very strange for main to return something, the logic should be wrapped in a different function and the error checking done from main.
If your program can fail, main should return an integer. This way everything you might use to invoke your code knows if it failed or works.

Not strange at all!

Instead of returning an exit code manually or finishing with 0 when you should not you can return an anyhow error instead.
Using `fn main() -> Result<(), Box<dyn Error>>` isn't necessarily unstylistic, the ability is there for a reason, but it is not great for CLI tools: it prints the `Debug` output of the error, which in many occasions is great for a developer to figure out what went wrong, but from subpar to terrible for end-users. The main benefit of having main return a `Result` is being able to write `?` and be done with it. A tool meant to be used by others like ripgrep? You better use a more explicit mechanism to ensure good output for wording, context and formatting.
Programs conventionally return an exit code or whatever it’s called.
This is a good intro and really highlights how Rust doesn't have a learning curve, it has a cliff. I'm tackling this in my own book [0] because I think the docs really throw you in the deep end (mixing metaphors). As this article shows, to implement a "hello world" you have to use a macro, so you can't even look for a function signature in the standard library docs to help. So you can either just say "don't worry about this for now, just trust me" or spend a whole chapter diving into macro syntax. And then it goes into errors, which in this example need to be boxed, so then you can spend a bunch of time explaining unsized types and boxing and memory allocation... or just say that it has to be boxed to keep the compiler happy.

Teaching Rust is basically a giant topological sorting exercise [1] where you're finding the optimal order to introduce syntax so that you steer clear of all of these rabbit holes. Or you just end up drawing the owl.

[0] https://rftgu.rs/ [1] https://en.wikipedia.org/wiki/Topological_sorting