Live data from Hacker News

Optimizing a Math Expression Parser in Rust

rpallas.xyz

11–20 of 60 posts

Re: Optimizing a Math Expression Parser in Rust

#12
post #7

Can somebody explain this line: n => Token::Operand(n.parse().unwrap()), How does the compiler derive the type of n?

We're doing a pattern match, so, this variable n has to be something that matches the entire value matched, its type will be identical to the type of the value matched, s a few lines earlier. That value is an item from the iterator we got from calling split_whitespace() and split_whitespace() returns a SplitWhiteSpace, a custom iterator whose items are themselves sub-strings of the input string with (no surprise) no…

Aha. But what type does n.parse() have then, and how does the compiler derive it?

Re: Optimizing a Math Expression Parser in Rust

#13
post #7

Can somebody explain this line: n => Token::Operand(n.parse().unwrap()), How does the compiler derive the type of n?

Operand(u32) (see definition of Token), n will be parsed as a u32. See here: https://doc.rust-lang.org/std/primitive.str.html#method.pars...

Re: Optimizing a Math Expression Parser in Rust

#14
post #7

Can somebody explain this line: n => Token::Operand(n.parse().unwrap()), How does the compiler derive the type of n?

maybe this isn't the question you meant to ask, but:

`n` has the same type as the input of the `match` block. In other words, it's a fallback case. (In this case, it's `&str`; the same as `"+"`, `"-"`, etc)

If you're wondering how `n.parse().unwrap()` has its type computed, well that part is because type inference is able to look at the definition of `Token::Operand(u32)` and discover that it's `u32`.

From my experience: The compiler can do this, as long as the first usage of the unknown-typed-thing gives it a type. If the first usage of it doesn't, then it won't try any harder to infer the type and it won't compile unless you add your own annotations on.

Re: Optimizing a Math Expression Parser in Rust

#15
post #7

Can somebody explain this line: n => Token::Operand(n.parse().unwrap()), How does the compiler derive the type of n?

maybe this isn't the question you meant to ask, but: `n` has the same type as the input of the `match` block. In other words, it's a fallback case. (In this case, it's `&str`; the same as `"+"`, `"-"`, etc) If you're wondering how `n.parse().unwrap()` has its type computed, well that part is because type inference is able to look at the definition of `Token::Operand(u32)` and discover that it's `u32`. From my experie…

Might also be useful for me to link to the docs for `parse` [1] and to the trait `FromStr` [2] that it relies on:

[1]: https://doc.rust-lang.org/std/primitive.str.html#method.pars... [2]: https://doc.rust-lang.org/std/str/trait.FromStr.html

Re: Optimizing a Math Expression Parser in Rust

#16

I am a bit surprised that the author didn't try to implement a stream parser. This could avoid loading the entire file in memory or relying on OS features like memory-mapped files.

A math expression is basically a tree but represented here as a string in a way that's probably impossible to stream.

Re: Optimizing a Math Expression Parser in Rust

#17
post #6

I am not even a newbye in Rust and also this could be just nitpicking, but it seems that match is comparing strings and not characters, if this is the case then I think Common Lisp can optimize more, since there is a special comparison for characters in CL. Edited: In the optimized version the author use bytes and generators and avoid using strings. I don't know if Rust generators are optimized for speed or memory, i…

If you wanted to match on characters (`char`s) then you could do this with single quotes (`'+'`)

Or if you wanted to do it on bytes, you could also do this, with (`b'+'`).

Unsure if that would provide a meaningful boost or not

Re: Optimizing a Math Expression Parser in Rust

#18
post #6

I am not even a newbye in Rust and also this could be just nitpicking, but it seems that match is comparing strings and not characters, if this is the case then I think Common Lisp can optimize more, since there is a special comparison for characters in CL. Edited: In the optimized version the author use bytes and generators and avoid using strings. I don't know if Rust generators are optimized for speed or memory, i…

> what happens when there is an error reading the file?

the question mark `?` denotes the fact that the error is bubbled up (kind of like an exception, but with stronger typing and less silent)

Re: Optimizing a Math Expression Parser in Rust

#19
post #6

I am not even a newbye in Rust and also this could be just nitpicking, but it seems that match is comparing strings and not characters, if this is the case then I think Common Lisp can optimize more, since there is a special comparison for characters in CL. Edited: In the optimized version the author use bytes and generators and avoid using strings. I don't know if Rust generators are optimized for speed or memory, i…

The ? will directly return Err if there is one during read_input_file(). This is just some syntactic sugar.

Re: Optimizing a Math Expression Parser in Rust

#20
post #12

Earlier quoted context omitted.

We're doing a pattern match, so, this variable n has to be something that matches the entire value matched, its type will be identical to the type of the value matched, s a few lines earlier. That value is an item from the iterator we got from calling split_whitespace() and split_whitespace() returns a SplitWhiteSpace, a custom iterator whose items are themselves sub-strings of the input string with (no surprise) no…

Aha. But what type does n.parse() have then, and how does the compiler derive it?

That function is returning a Vec, and so it knows the .collect() call needs to return a Vec, and so therefore the .map() function needs to return a Token. Therefore each match arm needs to return a Token too, so therefore the compiler selects the implementation of .parse() that returns Token.

I admit when I started rust, seeing calls to .parse() was one of the more confusing things I saw in rust code, because of how much it leans on type inference to be readable. In places like these, it's a bit more readable:

    let ip: IpAddr = ip_str.parse()?;
But when you see the .parse buried several levels deep and you have no idea what type it's trying to produce, it's a pain in the ass to read. This is why it's nice to use the turbo-fish syntax:

    let ip = ip_str.parse::()?;
Since you can drop .parse::()? anywhere to make the type explicit, especially when buried in type-inferred blocks like the code in TFA.
Post reply on HN