How to find an element in vector of Option<String> [duplicate] - vector

This question already has answers here:
Converting from Option<String> to Option<&str>
(5 answers)
Closed 1 year ago.
As a rust newbie I have a problem finding an element in a vector of Option using the "find" function:
#[derive(Debug)]
struct MyStruct {
name: Option<String>,
data: Option<String>
}
fn main() {
let my_vec=vec![
MyStruct{
name:Some(String::from("name1")),
data:Some(String::from("data1"))
},
MyStruct{
name:Some(String::from("name2")),
data:Some(String::from("data2"))
}
];
let search_string = String::from("name2");
let found_record=my_vec.iter().find(|s| s.name == Some(search_string));
println!("{:?}", found_record);
}
This code does not compile because of the comparison s.name == Some(search_string) because of a missing Copy trait. What would be the correct way for the "find" expression?

One option is to convert both your search_string and struct field to Option<&str> for comparison:
let found_record = my_vec.iter().find(
|s| s.name.as_deref() == Some(search_string.as_ref()));
playground

The Error cause
Note that you are constructing an Option by using Some() inside the closure by moving the String.
Some solutions
You can avoid that moving and make the closure capture by immutable reference by constructing the Option before it, as follows
let search_string = Some(String::from("name2"));
let found_record=my_vec.iter().find( |s| s.name == search_string);
Or enter inside the Option, as follows
let search_string = String::from("name2");
let found_record=my_vec.iter().find(
|s| match &s.name{Some(name)=> name, _ => return false,} == &search_string
);
I don't prefer the approach
let found_record = my_vec.iter().find(
|s| s.name.as_deref() == Some(search_string.as_ref()));
because it makes a new Some() every iteration unless if there is an optimization by the compiler.

Related

How do I convert an vector of variable addresses to an vector of variables? [duplicate]

This question already has answers here:
Why does iterating a vector of i32s give references to i32 (&i32)?
(2 answers)
What is an idiomatic way to collect an iterator of &T into a collection of Ts?
(2 answers)
Closed 1 year ago.
I am attempting to return a 2D array from a function with this prototype:
fn function1(rep1: usize, rep2: usize) -> (Vec<Vec<bool>>) {
There is this 2D vector struct in the function:
let mut array2d: Vec<Vec<bool>>;
I am using following itertools loop:
for perm in items.iter().permutations(items.len()).unique() {}
Each instance of perm is Vec<&bool> rather than Vec<bool> and so in every loop upon trying to push into array2d with array2d.push(perm); I get the following error:
note: expected struct `Vec<bool>` found struct `Vec<&bool>`
Initially I have attempted at modifying the return specifier to allow me to return 2D Vec of pointers but failed somehow at doing so. I'd be grateful if someone could show me how to dereference/cast vector of addresses as a vector of values and/or how to change return specifier so that I can return Vec<Vec<&bool>> rather than Vec<Vec<bool>>.
Full code snippet:
fn function1(rep1: usize, rep2: usize) -> (Vec<Vec<bool>>) {
let mut array2d: Vec<Vec<bool>>;
let mut items = vec![false; rep2];
for r in 0..(rep1 - 1){
items[r] = true;
}
for perm in items.iter().permutations(items.len()).unique() {
array2d.push(perm);
}
return array2d;
}

How to return a single element from a Vec from a function?

I'm new to Rust, and I'm trying to make an interface where the user can choose a file by typing the filename from a list of available files.
This function is supposed to return the DirEntry corresponding to the chosen file:
fn ask_user_to_pick_file(available_files: Vec<DirEntry>) -> DirEntry {
println!("Which month would you like to sum?");
print_file_names(&available_files);
let input = read_line_from_stdin();
let chosen = available_files.iter()
.find(|dir_entry| dir_entry.file_name().into_string().unwrap() == input )
.expect("didnt match any files");
return chosen
}
However, it appears chosen is somehow borrowed here? I get the following error:
35 | return chosen
| ^^^^^^ expected struct `DirEntry`, found `&DirEntry`
Is there a way I can "unborrow" it? Or do I have to implement the Copy trait for DirEntry?
If it matters I don't care about theVec after this method, so if "unborrowing" chosen destroys the Vec, thats okay by me (as long as the compiler agrees).
Use into_iter() instead of iter() so you get owned values instead of references out of the iterator. After that change the code will compile and work as expected:
fn ask_user_to_pick_file(available_files: Vec<DirEntry>) -> DirEntry {
println!("Which month would you like to sum?");
print_file_names(&available_files);
let input = read_line_from_stdin();
let chosen = available_files
.into_iter() // changed from iter() to into_iter() here
.find(|dir_entry| dir_entry.file_name().into_string().unwrap() == input)
.expect("didnt match any files");
chosen
}

Vector store mixed types of data in Rust [duplicate]

This question already has an answer here:
How do I create a heterogeneous collection of objects?
(1 answer)
Closed 5 years ago.
In the context of converting a infix expression to a postfix one, using the Shunting-yard algorithm. I want to use a vector to store the output, which would store both operator and numeric type data.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Operator {
Add,
Sub,
Mul,
Div,
}
fn main() {
let mut output: Vec<String> = Vec::new(); // create an output vector
let a = 2;
let b = Operator::Add;
let c = 3;
output.push(a.to_string());
output.push(b.to_string());
output.push(c.to_string());
}
This above code of course doesn't compile, since the to_string() method is not defined for Operator. I see two ways to fix it:
Define a to_string() method
Create a vector to store references to numbers and Operator.
I think the second is the preferred choice, though I don't know if creating a vector of references will introduce lots of complexity.
There's no need to use references; just store the numbers and Operators directly in an enum:
enum Thing {
Op(Operator),
Number(i32),
}
fn main() {
let mut output: Vec<Thing> = Vec::new();
let a = 2;
let b = Operator::Add;
let c = 3;
output.push(Thing::Number(a));
output.push(Thing::Op(b));
output.push(Thing::Number(c));
}
And then match on them when taking them out.

How can I handle "if may be missing an else clause" when using an if inside of a map?

I'm working on a code challenge which will detect case-insensitive anagrams of a given word from a list of words.
My first cut is to use something like this:
pub fn anagrams_for(s: &'static str, v: &[&'static str]) -> Vec<&'static str> {
let mut outputs: Vec<&str> = vec![];
// Find the case-insensitive, sorted word to check
let mut s_sorted: Vec<_> = s.to_string().to_lowercase().chars().collect();
s_sorted.sort();
for word in v {
// Case-desensitize and sort each word in the slice
let mut word_sorted: Vec<_> = word.to_string().to_lowercase().chars().collect();
word_sorted.sort();
// if the case-insensitive words are the same post sort and not presort (to avoid self-anagrams), add it to the vector
if word_sorted == s_sorted && s.to_string().to_lowercase() != word.to_string().to_lowercase() {
outputs.push(word)
}
}
outputs
}
This works as expected, but is not very idiomatic. I'm now trying a second iteration which uses more functional features of Rust:
pub fn anagrams_for(s: &'static str, v: &[&'static str]) -> Vec<&'static str> {
let mut s_sorted: Vec<_> = s.to_string().to_lowercase().chars().collect();
s_sorted.sort();
v.iter().map(&|word: &str| {
let mut word_sorted: Vec<_> = word.to_string().to_lowercase().chars().collect();
word_sorted.sort();
if word_sorted == s_sorted && s.to_string().to_lowercase() != word.to_string().to_lowercase() {
word
}
}).collect()
}
I'm currently getting a few errors (most of which I could likely resolve), but the one I'm interested in solving is
if may be missing an else clause:
expected `()`,
found `&str`
(expected (),
found &-ptr) [E0308]
This is because in the case of a non-anagram, map attempts to push something into the vector (seemingly ()).
How can I handle this? It's possible that map isn't the best idiom because it requires some operation to be performed on each element in a list, not a subset (maybe filter?).
As you noticed, the problem is that in the non-anagram-case your closure (the || { ... } block) doesn't return a value.
You can solve this by using filter_map instead of map. That function takes a closure that returns Option<U> instead of U, so the last expression of your closure looks something like:
if /* ... */ {
Some(word)
} else {
None
}
Unrelated to the main question, some notes on your code:
You can remove the .to_string() calls before .to_lowercase() calls. the latter method belongs to the type str, so it works fine. Calling to_string() adds unnecessary allocations.
the & in front of the closure (&|...|) can most probably be removed...
... as can the : &str type annotation in the closures argument list

How do I convert a vector of strings to a vector of integers in a functional way?

I'm trying to convert Vec<&str> to Vec<u16> but I can't figure out a functional way to do it.
let foo: &str = "1,2,3"; // Parsing a string here
let bar: Vec<&str> = foo.split(",").collect(); // Bar is a nice vector of &str's
I need to get bar into a Vec<u16>.
There's an iterator adapter map! You'd use it like this:
let bar: Vec<u16> = foo.split(",").map(|x| x.parse::<u16>().unwrap()).collect();
parse is a library function that relies on the trait FromStr, and it can return an error, so we need to unwrap() the error type. (This is a good idea for a short example, but in real code, you will want to handle the error properly - if you have a value that's not a u16 there, your program will just crash).
map takes a closure that takes it's parameter by value and then returns the iterator obtained by lazily applying that function. You're collecting all of the values here, but if you only take(5) of them, you would only parse 5 of the strings.
You haven't fully specified your problem. Specifically, what should happen when one of the strings cannot be parsed into a number? When you parse a number from a string using parse, it can fail. That is why the function returns a Result:
fn parse<F>(&self) -> Result<F, F::Err>
where
F: FromStr,
Here's a solution that takes the vector, gets an iterator with iter, changes each item using map and ultimately returns a Result using collect. If the parsing was a success, you get an Ok. If any failed, you get an Err:
fn main() {
let input = "1,2,3";
let strings: Vec<_> = input.split(",").collect();
let numbers: Result<Vec<u16>, _> = strings.iter().map(|x| x.parse()).collect();
println!("{:?}", numbers);
}
Or you could remove failed conversions by filtering out Err values with flat_map:
fn main() {
let input = "1,2,3";
let strings: Vec<_> = input.split(",").collect();
let numbers: Vec<u16> = strings.iter().flat_map(|x| x.parse()).collect();
println!("{:?}", numbers);
}
Of course, it's a bit silly to convert the string into a vector of strings and then convert it again to a vector of integers. If you actually have a comma-separated string and want numbers, do it in one go:
fn main() {
let input = "1,2,3";
let numbers: Result<Vec<u16>, _> = input.split(",").map(|x| x.parse()).collect();
println!("{:?}", numbers);
}
See also:
Why does `Option` support `IntoIterator`?
My take as someone not really experienced in Rust yet.
fn main() {
let foo: &str = "1,2,3"; // Parsing a string here
let bar: Vec<&str> = foo.split(",").collect(); // Bar is a nice vector of &str's
// here the magic happens
let baz = bar.iter().map(|x| x.parse::<i64>());
for x in baz {
match x {
Ok(i) => println!("{}", i),
Err(_) => println!("parse failed"),
}
}
}
Note that since parse returns a Result, you have to extract the value from each parsed element. You might want to behave in a different way, e.g. filter only the succeeded results.

Resources