139 ms·
> If you aren’t familiar with Rust syntax, |argument| result is an anonymous function equivalent to function myfunction(argument) { return result; }. > Here, y
by ekusiadadus 1y ago
> If you aren’t familiar with Rust syntax, |argument| result is an anonymous function equivalent to function myfunction(argument) { return result; }.
> Here, your program is constructed left to right. The first time you type line is the declaration of the variable. As soon as you type line., your editor is able to suggest available methods.
Yeah, having LSP autocomplete here does feel nice.
But it also makes the code harder to scan than Python. Quick readability at a glance seems like the bigger win than just better autocomplete.
- chrismorgan 1y ago> But it also makes the code harder to scan than Python. Quick readability at a glance seems like the bigger win than just better autocomplete. It depends a lot on what you’re accustomed to. You get used to whichever style. Just like different languages use different sentence order: subject, object and verb appear in all possible orders in different languages, and their speakers get along just fine. There are some situations where one is clearly superior to the other, and vice versa. `text.lines().map(|line| line.split_whitespace())` can be read loosely as “take text; take its lines; map each line, split it on whitespace”. Straightforward and matching execution flow. `[line.split() for line in text.splitlines()]` doesn’t read so elegantly left-to-right, but so long as it’s small enough you spot the `for` token, realise you’re dealing with a list comprehension, and read it loosely from left to right as “we have a list made up of splitting each line, where lines come from text, split”. Execution-wise, you execute `text.splitlines()`, then `for line in`, then `line.split()`. It’s a bunch of left-to-rights embedded in a right-to-left. This has long been noted as a hazard of list comprehensions, especially the confusion you end up with with nested ones. Now you could quibble over my division of `for line in text.splitlines()` into two runs; but I think it’s fair. Consider how in Rust you get both `for line in text.split_lines() { … }` and `text.split_lines().for_each(|line| { … })`. Sometimes the for block reads better, sometimes .for_each() or .map() or whatever does. (But map(lambda …: …, …) never really does.) Python was my preferred language from 2009–2013 and I still use it not infrequently, but Rust has been my preferred language ever since. I can say: I find the Rust version significantly easier to read, in this particular case. I think the fact there are two levels of split contributes to this.
- xigoi 1y agoOn the other hand, in Rust you often have to add something like .unwrap().unwrap().into::<&&&Vec<Box<&&&&&mut String>>>() which kind of ruins the elegance :P
- super_flanker 1y agoIt indeed doesn't look elegant, however I've never in my experience seen a usage like this. Do you have any reference where you might have seen this kind of usage.
- xigoi 1y agoThis is not meant to be taken literally, I was making fun of how Rust often requires a lot of punctuation and thinking about details of memory allocation.
- super_flanker 1y agoGotcha, yeah you got me there with so many `&`. Good one.
- Tuna-Fish 1y agoOften, such as this time. :) The Rust and Python code are not equivalent: The Python code instantly produces the nested list. Rust map does not iterate over the list given to it, it only produces an iterator that you then have to drain. To make them equivalent, you need to add collect calls. ... which adds more typing, because then collect needs to know the types you want to collect into. To make the Rust code fully equivalent with the Python version, you need to do: let words_on_lines: Vec<Vec<&str>> = text.lines().map(|line| line.split_whitespace().collect()).collect(); // alternatively, you can put the type arguments into the .collect() calls: let words_on_lines = text.lines().map(|line| line.split_whitespace().collect::<Vec<_>>()).collect::<Vec<_>>(); // which approaches the level of noise you expect from rust, // but stylistically I much prefer having the type declaration near the definition. But of course this example also highlights Rust's power. Often (usually) you don't need to do that, and you can instead use the iterator directly, saving all the intermediate allocations. With Rust, I can often write the kind of code I do in python, but without a single heap allocation. Also, it doesn't bind you to a single collection type that ships with the language; if you want to, you can easily use something like a vec with a short vec optimization instead. But it definitely does inherit Perl's mantle as executable line noise.