What is a clean way to convert a Result into an Option? - rust

Before updating to a more recent Rust version the following used to work:
fn example(val: &[&str]) {
let parsed_value: Vec<usize> = val
.iter()
.filter_map(|e| e.parse::<usize>())
.collect();
}
However, now the parse method returns a Result type instead of an Option and I get the error:
error[E0308]: mismatched types
--> src/lib.rs:4:25
|
4 | .filter_map(|e| e.parse::<usize>())
| ^^^^^^^^^^^^^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result`
|
= note: expected type `std::option::Option<_>`
found type `std::result::Result<usize, std::num::ParseIntError>`
I could create an Option through a conditional, but is there a better / cleaner way?

Use Result::ok. Types added for clarity:
let res: Result<u8, ()> = Ok(42);
let opt: Option<u8> = res.ok();
println!("{:?}", opt);
For symmetry's sake, there's also Option::ok_or and Option::ok_or_else to go from an Option to a Result.
In your case, you have an iterator.
If you'd like to ignore failures, use Iterator::flat_map. Since Result (and Option) implement IntoIterator, this works:
let parsed_value: Vec<usize> = val
.iter()
.flat_map(|e| e.parse())
.collect();
If you'd like to stop on the first failure, you can collect into one big Result. This is less obvious, but you can check out the implementors of FromIterator for the full list of collect-able items.
let parsed_value: Result<Vec<usize>, _> = val
.iter()
.map(|e| e.parse())
.collect();
Of course, you can then convert the one big Result into an Option, as the first example shows.

Related

POLARS Dataframe innerJOIN in RUST

RUST / POLARS nooby question :)
I can not get the "inner_join" to work:
use polars::prelude::*;
use std::fs::File;
use std::path::PathBuf;
use std::env;
fn main() -> std::io::Result<()> {
let mut root = env::current_dir().unwrap();
let file_1 = root.join("data_1.csv");
let file_2 = root.join("data_2.csv");
// Get data from first file (one column data: column_1)
let file = File::open(file_1).expect("Cannot open file.");
let first_data = CsvReader::new(file)
.has_header(false)
.finish()
.unwrap();
// WORKS !
println!("{}", first_data);
// Get data from second file (one column data: column_1)
let file = File::open(file_2).expect("Cannot open file.");
let second_data = CsvReader::new(file)
.has_header(false)
.finish()
.unwrap();
// WORKS !
println!("{}", second_data);
// Trying to get an INNER join
let all_data = first_data.inner_join(second_data, "column_1", "column_1");
println!("{}", all_data);
Ok(())
}
BUILD OUTPUT:
error[E0277]: `&str` is not an iterator
--> src\main.rs:33:31
|
33 | let all_data = first_data.inner_join(second_data, "column_1", "column_1");
| ^^^^^^^^^^ `&str` is not an iterator; try calling `.chars()` or `.bytes()`
|
= help: the trait `Iterator` is not implemented for `&str`
= note: required because of the requirements on the impl of `IntoIterator` for `&str`
note: required by a bound in `hash_join::<impl DataFrame>::inner_join`
--> C:\Users\rnio\.cargo\registry\src\github.com-1ecc6299db9ec823\polars-core-0.23.1\src\frame\hash_join\mod.rs:645:12
|
645 | I: IntoIterator<Item = S>,
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `hash_join::<impl DataFrame>::inner_join`
Looking for any hint / information what I am missing ... I looked at POLARS Features ... and could not see a flag needed to do JOIN operations ... any ideas ?
Thanks in advance :)
The problem is with the columns, not with the DataFrame.
The inner_join function takes in the DataFrame and two sets of columns that implement IntoIterator. Because you are passing in strings for the column names, it's giving you the error telling you to call .chars() to turn it into an iterator over the characters.
You should be able to get this to work with the following:
let all_data = first_data.inner_join(&second_data, ["column_1"], ["column_1"]);
You can see the definition of this function here: https://docs.rs/polars/latest/polars/frame/struct.DataFrame.html#method.inner_join

In Rust, what's the correct way to keep data read from a file in scope?

I'm new to Rust.
I'm reading SHA1-as-hex-strings from a file - a lot of them, approx. 30 million.
In the text file, they are sorted ascending numerically.
I want to be able to search the list, as fast as possible.
I (think I) want to read them into a (sorted) Vec<primitive_type::U256> for fast searching.
So, I've tried:
log("Loading haystack.");
// total_lines read earlier
let mut the_stack = Vec::<primitive_types::U256>::with_capacity(total_lines);
if let Ok(hay) = read_lines(haystack) { // Get BufRead
for line in hay { // Iterate over lines
if let Ok(hash) = line {
the_stack.push(U256::from(hash));
}
}
}
log(format!("Read {} hashes.", the_stack.len()));
The error is:
$ cargo build
Compiling nsrl v0.1.0 (/my_app)
error[E0277]: the trait bound `primitive_types::U256: std::convert::From<std::string::String>` is not satisfied
--> src/main.rs:55:24
|
55 | the_stack.push(U256::from(hash));
| ^^^^^^^^^^ the trait `std::convert::From<std::string::String>` is not implemented for `primitive_types::U256`
|
= help: the following implementations were found:
<primitive_types::U256 as std::convert::From<&'a [u8; 32]>>
<primitive_types::U256 as std::convert::From<&'a [u8]>>
<primitive_types::U256 as std::convert::From<&'a primitive_types::U256>>
<primitive_types::U256 as std::convert::From<&'static str>>
and 14 others
= note: required by `std::convert::From::from`
This code works if instead of the variable hash I have a string literal, e.g. "123abc".
I think I should be able to use the implementation std::convert::From<&'static str>, but I don't understand how I'm meant to keep hash in scope?
I feel like what I'm trying to achieve is a pretty normal use case:
Iterate over the lines in a file.
Add the line to a vector.
What am I missing?
You almost want something like,
U256::from_str(&hash)?
There is a conversion from &str in the FromStr trait called from_str. It returns a Result<T, E> value, because parsing a string may fail.
I think I should be able to use the implementation std::convert::From<&'static str>, but I don't understand how I'm meant to keep hash in scope?
You can’t keep the hash in scope with 'static lifetime. It looks like this is a convenience method to allow you to use string constants in your program—but it is really nothing more than U256::from_str(&hash).unwrap().
However…
If you want a SHA-1, the best type is probably [u8; 20] or maybe [u32; 5].
You want a base 16 decoder, something like base16::decode_slice. Here’s how that might look in action:
/// Error if the hash cannot be parsed.
struct InvalidHash;
/// Type for SHA-1 hashes.
type SHA1 = [u8; 20];
fn read_hash(s: &str) -> Result<SHA1, InvalidHash> {
let mut hash = [0; 20];
match base16::decode_slice(s, &mut hash[..]) {
Ok(20) => Ok(hash),
_ => Err(InvalidHash),
}
}

Finding most frequently occurring string in a structure in Rust?

I'm looking for the string which occurs most frequently in the second part of the tuple of Vec<(String, Vec<String>)>:
use itertools::Itertools; // 0.8.0
fn main() {
let edges: Vec<(String, Vec<String>)> = vec![];
let x = edges
.iter()
.flat_map(|x| &x.1)
.map(|x| &x[..])
.sorted()
.group_by(|x| x)
.max_by_key(|x| x.len());
}
Playground
This:
takes the iterator
flat-maps to the second part of the tuple
turns elements into a &str
sorts it (via itertools)
groups it by string (via itertools)
find the group with the highest count
This supposedly gives me the group with the most frequently occurring string, except it doesn't compile:
error[E0599]: no method named `max_by_key` found for type `itertools::groupbylazy::GroupBy<&&str, std::vec::IntoIter<&str>, [closure#src/lib.rs:9:19: 9:24]>` in the current scope
--> src/lib.rs:10:10
|
10 | .max_by_key(|x| x.len());
| ^^^^^^^^^^
|
= note: the method `max_by_key` exists but the following trait bounds were not satisfied:
`&mut itertools::groupbylazy::GroupBy<&&str, std::vec::IntoIter<&str>, [closure#src/lib.rs:9:19: 9:24]> : std::iter::Iterator`
I'm totally lost in these types.
You didn't read the documentation for a function you are using. This is not a good idea.
This type implements IntoIterator (it is not an iterator itself),
because the group iterators need to borrow from this value. It should
be stored in a local variable or temporary and iterated.
Personally, I'd just use a BTreeMap or HashMap:
let mut counts = BTreeMap::new();
for word in edges.iter().flat_map(|x| &x.1) {
*counts.entry(word).or_insert(0) += 1;
}
let max = counts.into_iter().max_by_key(|&(_, count)| count);
println!("{:?}", max);
If you really wanted to use the iterators, it could look something like this:
let groups = edges
.iter()
.flat_map(|x| &x.1)
.sorted()
.group_by(|&x| x);
let max = groups
.into_iter()
.map(|(key, group)| (key, group.count()))
.max_by_key(|&(_, count)| count);

Why can't I collect the Lines iterator into a vector of Strings?

I'm trying to read the lines of a text file into a vector of Strings so I can continually loop over them and write each line to a channel for testing, but the compiler complains about collect:
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::path::Path;
fn main() {
let file = File::open(Path::new("file")).unwrap();
let reader = BufReader::new(&file);
let _: Vec<String> = reader.lines().collect().unwrap();
}
The compiler complains:
error[E0282]: type annotations needed
--> src/main.rs:9:30
|
9 | let lines: Vec<String> = reader.lines().collect().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `B`
|
= note: type must be known at this point
Without the .unwrap(), compiler says:
error[E0277]: a collection of type `std::vec::Vec<std::string::String>` cannot be built from an iterator over elements of type `std::result::Result<std::string::String, std::io::Error>`
--> src/main.rs:9:45
|
9 | let lines: Vec<String> = reader.lines().collect();
| ^^^^^^^ a collection of type `std::vec::Vec<std::string::String>` cannot be built from `std::iter::Iterator<Item=std::result::Result<std::string::String, std::io::Error>>`
|
= help: the trait `std::iter::FromIterator<std::result::Result<std::string::String, std::io::Error>>` is not implemented for `std::vec::Vec<std::string::String>`
How do I tell Rust the correct type?
Since you want to collect straight into a Vec<String> while the Lines iterator is over Result<String, std::io::Error>, you need to help the type inference a little bit:
let lines: Vec<String> = reader.lines().collect::<Result<_, _>>().unwrap();
or even just:
let lines: Vec<_> = reader.lines().collect::<Result<_, _>>().unwrap();
This way the compiler knows that there is an intermediate step with a Result<Vec<String>, io::Error>. I think this case could be improved in the future, but for now the type inference is not able to deduce this.

How do I find the index of an element in an array, vector or slice?

I need to find an index of an element in a vector of strings. This is what I got so far:
fn main() {
let test: Vec<String> = vec![
"one".to_string(),
"two".to_string(),
"three".to_string(),
"four".to_string(),
];
let index: i32 = test
.iter()
.enumerate()
.find(|&r| r.1.to_string() == "two".to_string())
.unwrap()
.0;
}
It produces an error:
error[E0308]: mismatched types
--> src/main.rs:9:22
|
9 | let index: i32 = test
| ______________________^
10 | | .iter()
11 | | .enumerate()
12 | | .find(|&r| r.1.to_string() == "two".to_string())
13 | | .unwrap()
14 | | .0;
| |__________^ expected i32, found usize
I assume that's because enumerate() returns a tuple of (usize, _) (correct me if I'm wrong), but how do I convert usize to i32 here? If there is a better approach, I'm open to suggestions.
I think you should look at the position method instead.
fn main() {
let test = vec!["one", "two", "three"];
let index = test.iter().position(|&r| r == "two").unwrap();
println!("{}", index);
}
You can test it here.
Note that this works for any iterator, so it can be used for vectors, arrays, and slices, all of which produce iterators.
TLDR Use an iterator with the position method, the Rust docs shows a good example.
No, it's because indices are usize, not i32. In fact, i32 is completely inappropriate for this purpose; it may not be large enough, and there's no reason for it to be signed. Just use usize.
Some other notes: calling to_string() is not free, and you don't need it for the comparison; you can compare string slices just fine!
Also, if you really want to turn a usize into an i32, you can do that with a cast: x as i32, though this will not produce an error on over- or under-flow (i.e. the result may be negative).
All that said, as noted in Mathieu David's answer, there's a position method on iterators that does what you want.

Resources