A small summary for idiomatic Rust
I got myself into simple problem: to split string into pieces by space. During this …ehem… problem I found I need to filter empty splits if there is more than one space in row.
My intial code was:
foo.split(" ").filter(|e| e.len() > 0).collect()
I asked on Reddit if it can be shorter, and after few discussion twists of different hospitality, there were few important takeaways I want to summarize.
Split
There are few helpful functions of splitting, and most of them are not in String
, but in str
.
split
(the one I started with)split_whitespace
(which honor all unicode spaces and consumes multiple spaces without producing empty&str
).split_ascii_whitespace
(which honor ascii-only-whitespaces). My problem is sovled by it.
Also, it’s better to split with ' '
, and not with " "
(split by char
, not by &str
), which is subtle, but important for performance.
(But discussion continued!)
Filter
Th expression |e| e.len() > 0
is ugly. But of course there is a better option: |e| !e.is_empty()
.
Also, flatten
(which is not applicable in my case) is skipping empty Option/Result.
Truthy
One thing I said in discussion is comparison with truthy Python. I got reprimanded for that and I listened for arguments about semi-automatic casting to booleans. It’s important.
Missing things in Rust
The discussion also leaded to some ideas:
- Can we have
skip_default
iterator which skip default value? Most types hasDefault
trait, so it’s easy to implement.Default+PartEq
… .len
and.is_empty
, basically, sounds like a part of a trait. But there is none.Container
may be a good trait… And then you can have.skip_empty
implemented forItem=T: Container
.
It is not there, but worth think and reason about. May be they should be? Or may be they are redundant.
Conclusion
There is no specific conclusion, but attention to small things let intuition to grow. I got slightly more familiar with iterators over str
(and that they are in str
, not in String
is important!), and got better view on types.