How to extract elements of a vector of strings in Rust? - vector

Let's say I have the following code:
fn extract() -> Vec<String> {
let data = vec!["aaa".to_string(), "bbb".to_string(), "ccc".to_string()];
vec![data[0], data[2]]
}
In practice, I read data from a file.
Obviously, this doesn't compile because I'm pulling strings out of the vector data, leaving the vector in an undefined state. But, conceptually, it should work, because I'm not using data afterwards anyway.
I can use mem::replace, but this seems crazy:
fn extract() -> Vec<String> {
let mut data = vec!["aaa".to_string(), "bbb".to_string(), "ccc".to_string()];
let a = mem::replace(&mut data[0], "".to_string());
let c = mem::replace(&mut data[2], "".to_string());
vec![a, c]
}
How do I go about extracting specific elements from the vector without having to clone the strings?

Vec has special methods for that. swap_remove, remove (warning, linear complexity), drain. For example,
fn extract() -> Vec<String> {
let mut data = vec!["aaa".to_string(), "bbb".to_string(), "ccc".to_string()];
// order does matter
vec![data.swap_remove(2), data.swap_remove(0)]
}

You cannot have "holes" in a vector. So when you move something out of it, you either change the indices of the remaining elements (using remove or swap_remove), or replace it with something else.
Why don't you just consume the vector in order and ignore what you don't need? If you need to save some of the elements for later use, you can use data.iter().filter(...).collect(). If you really want to avoid copying any strings, you can wrap them in Rc so that only pointers are copied.

Related

What is the right way to have multiple linked lists and move data between them in Rust?

What is the right way to have multiple std::collections::LinkedLists where the number of those lists is unknown at compile time?
I'm filling them with data as well as merging them (e.g. using append()).
I thought it would be good to have a vector that contains those lists, or contains references to those lists.
I have tried the following:
use std::collections::LinkedList;
fn listtest() {
let mut v: Vec<LinkedList<i32>> = Vec::new();
v.push(LinkedList::new()); // first list
v.push(LinkedList::new()); // second list
v[0].push_back(1); // fill with data
v[1].push_back(3); // fill with data
v[0].append(&mut v[1]); // merge lists
}
fn main() {
listtest();
}
This fails to compile because I have two mutable references of v when using append(). I also tried using Vec<&mut LinkedList<i32>>, but did not succeed.
What would be the right approach to this problem?
There is no right approach. One possibility is to use split_at_mut. This creates two separate slices, each of which can be mutated separately from the other:
use std::collections::LinkedList;
fn main() {
let mut v = vec![LinkedList::new(), LinkedList::new()];
v[0].push_back(1);
v[1].push_back(3);
{
let (head, tail) = v.split_at_mut(1);
head[0].append(&mut tail[0]);
}
println!("{:?}", v);
}
See:
How to get mutable references to two array elements at the same time?
How can I write data from a slice to the same slice?
How to operate on 2 mutable slices of a Rust array
etc.
Most collections have an iter_mut method that returns an iterator that yields mutable references to each item in the collection. And these references can all be used at the same time! (But the references must come from the same iterator; you can't use references coming from separate calls to iter_mut concurrently.)
use std::collections::LinkedList;
fn listtest() {
let mut v: Vec<LinkedList<i32>> = Vec::new();
v.push(LinkedList::new()); // first list
v.push(LinkedList::new()); // second list
v[0].push_back(1); // fill with data
v[1].push_back(3); // fill with data
let mut vi = v.iter_mut();
let first = vi.next().unwrap();
let second = vi.next().unwrap();
first.append(second); // merge lists
}
fn main() {
listtest();
}
Also remember that iterators have the nth method for doing the equivalent of next in a loop.

How can I search through a 2D array without for loops?

I have a Vec<Vec<char>> and I want to find all the x,y positions of a specific character, let's say 'x'. I can use a double for loop with enumerate and manually build up the solution (and I would guess this is the sane thing to do), but is there a nice way to do it with nothing but iterators?
More or less I'm looking for ways to clean this up:
let locs: Vec<(usize, (usize, &char))> = grid.iter()
.enumerate()
.flat_map(|(ind, row)|
iter::repeat(ind)
.zip(row.iter()
.enumerate()))
.filter(|&(x, (y, ch))| ch == 'x')
.collect();
For one, is there a way to flatten the tuples?
Here's my attempt, which does flatten the tuples:
let locs: Vec<(usize, usize, char)> = grid.iter()
.enumerate()
.flat_map(|(y, row)| {
row.iter()
.enumerate()
.map(move |(x, &c)| (x,y,c))
})
.filter(|&(_,_,c)| c == 'x')
.collect();
println!("{:?}", locs)
Playground
My approach was to first flatten to (x,y,c) and then filter. I took the liberty of returning the actual chars rather than references.
The move closure was needed because otherwise the inner closure (which lives longer, inside the iterator, than the outer closure) had a reference to the outer y.
If I wanted to do this more often, I would write an Iterator implementation which let me do:
let locs: Vec<(usize, usize, char) =
iter2d(grid)
.filter(&|_,_,c| c == 'x')
.collect();
The implementation is left as an exercise for the reader. :-)

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 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.

Concatenate a vector of vectors of strings

I'm trying to write a function that receives a vector of vectors of strings and returns all vectors concatenated together, i.e. it returns a vector of strings.
The best I could do so far has been the following:
fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
let vals : Vec<&String> = vecs.iter().flat_map(|x| x.into_iter()).collect();
vals.into_iter().map(|v: &String| v.to_owned()).collect()
}
However, I'm not happy with this result, because it seems I should be able to get Vec<String> from the first collect call, but somehow I am not able to figure out how to do it.
I am even more interested to figure out why exactly the return type of collect is Vec<&String>. I tried to deduce this from the API documentation and the source code, but despite my best efforts, I couldn't even understand the signatures of functions.
So let me try and trace the types of each expression:
- vecs.iter(): Iter<T=Vec<String>, Item=Vec<String>>
- vecs.iter().flat_map(): FlatMap<I=Iter<Vec<String>>, U=???, F=FnMut(Vec<String>) -> U, Item=U>
- vecs.iter().flat_map().collect(): (B=??? : FromIterator<U>)
- vals was declared as Vec<&String>, therefore
vals == vecs.iter().flat_map().collect(): (B=Vec<&String> : FromIterator<U>). Therefore U=&String.
I'm assuming above that the type inferencer is able to figure out that U=&String based on the type of vals. But if I give the expression the explicit types in the code, this compiles without error:
fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
let a: Iter<Vec<String>> = vecs.iter();
let b: FlatMap<Iter<Vec<String>>, Iter<String>, _> = a.flat_map(|x| x.into_iter());
let c = b.collect();
print_type_of(&c);
let vals : Vec<&String> = c;
vals.into_iter().map(|v: &String| v.to_owned()).collect()
}
Clearly, U=Iter<String>... Please help me clear up this mess.
EDIT: thanks to bluss' hint, I was able to achieve one collect as follows:
fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
vecs.into_iter().flat_map(|x| x.into_iter()).collect()
}
My understanding is that by using into_iter I transfer ownership of vecs to IntoIter and further down the call chain, which allows me to avoid copying the data inside the lambda call and therefore - magically - the type system gives me Vec<String> where it used to always give me Vec<&String> before. While it is certainly very cool to see how the high-level concept is reflected in the workings of the library, I wish I had any idea how this is achieved.
EDIT 2: After a laborious process of guesswork, looking at API docs and using this method to decipher the types, I got them fully annotated (disregarding the lifetimes):
fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
let a: Iter<Vec<String>> = vecs.iter();
let f : &Fn(&Vec<String>) -> Iter<String> = &|x: &Vec<String>| x.into_iter();
let b: FlatMap<Iter<Vec<String>>, Iter<String>, &Fn(&Vec<String>) -> Iter<String>> = a.flat_map(f);
let vals : Vec<&String> = b.collect();
vals.into_iter().map(|v: &String| v.to_owned()).collect()
}
I'd think about: why do you use iter() on the outer vec but into_iter() on the inner vecs? Using into_iter() is actually crucial, so that we don't have to copy first the inner vectors, then the strings inside, we just receive ownership of them.
We can actually write this just like a summation: concatenate the vectors two by two. Since we always reuse the allocation & contents of the same accumulation vector, this operation is linear time.
To minimize time spent growing and reallocating the vector, calculate the space needed up front.
fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
let size = vecs.iter().fold(0, |a, b| a + b.len());
vecs.into_iter().fold(Vec::with_capacity(size), |mut acc, v| {
acc.extend(v); acc
})
}
If you do want to clone all the contents, there's already a method for that, and you'd just use vecs.concat() /* -> Vec<String> */
The approach with .flat_map is fine, but if you don't want to clone the strings again you have to use .into_iter() on all levels: (x is Vec<String>).
vecs.into_iter().flat_map(|x| x.into_iter()).collect()
If instead you want to clone each string you can use this: (Changed .into_iter() to .iter() since x here is a &Vec<String> and both methods actually result in the same thing!)
vecs.iter().flat_map(|x| x.iter().map(Clone::clone)).collect()

Resources