How to pass one Vec to multiple functions in Rust? - vector

I wrote a max function which takes a Vec as a parameter. It works as I expected. Then I added a min function the same as the max function:
fn main() {
let my_array = vec![61, 14, 71, 23, 42, 8, 13, 66];
let max = max(my_array);
let min = min(my_array);
println!("Max value is {}.", max);
}
fn max(array: Vec<i32>) -> i32 {
let mut max = array[0];
for val in array {
if max < val {
max = val;
}
}
max
}
fn min(array: Vec<i32>) -> i32 {
let mut min = array[0];
for val in array {
if min > val {
min = val;
}
}
min
}
Rust reports an error if I put the same my_array parameter on the call to min:
error[E0382]: use of moved value: `my_array`
--> src/main.rs:4:19
|
2 | let my_array = vec![61, 14, 71, 23, 42, 8, 13, 66];
| -------- move occurs because `my_array` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
3 | let max = max(my_array);
| -------- value moved here
4 | let min = min(my_array);
| ^^^^^^^^ value used here after move
How can I write code that works?

This is an issue that very beginners to Rust will experience. As a beginner, you should read The Rust Programming Language. A lot of effort has been put into this book, especially for newcomers to Rust. This will cover many things that you will run into.
Relevant sections:
Data Types
Understanding Ownership
References and borrowing
The underlying problem is that you've transferred ownership of the vector when you call max. The value is then gone; main no longer has it.
The simplest thing to to is to clone the vector before passing to max. This allows main to keep ownership of my_array and then transfer ownership to min on the subsequent line:
let max = max(my_array.clone());
let min = min(my_array);
This is inefficient, as neither max nor min need to take ownership of the vector to do their work. Cloning the Vec also requires additional memory allocation. It's more idiomatic to pass in a slice, which is a type of reference to the data inside the Vec:
let max = max(&my_array);
let min = min(&my_array);
fn max(array: &[i32]) -> i32 {
let mut max = array[0];
for &val in array {
if max < val {
max = val;
}
}
max
}
When iterating over a slice, you get back references to the items in the slice. With integers, we can dereference them (here using the & in for &val in array) and make a copy of the value.
See also:
Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?
Even better, there's no need to rewrite basic functions like this. You also assume there's always at least one value, which isn't true for an empty vector. The idiomatic solution is to use iterators:
fn main() {
let my_array = vec![61, 14, 71, 23, 42, 8, 13, 66];
let max = my_array.iter().max();
let min = my_array.iter().min();
println!("Max value is {:?}.", max);
println!("Min value is {:?}.", min);
}
This uses Iterator::min and Iterator::max,
which each return an Option, as an empty slice has no minimum or maximum value.
Technically, it's a little different from your original solution, as min and max are Option<&i32>; a reference to the original slice. You can get back to Option<i32> by using Option::copied:
fn main() {
let my_array = vec![61, 14, 71, 23, 42, 8, 13, 66];
let max = my_array.iter().max().copied();
let min = my_array.iter().min().copied();
println!("Max value is {:?}.", max);
println!("Min value is {:?}.", min);
}
Bonus information: slices, Vecs, and arrays are all different types. It's not correct to refer to my_array as an array at all.

Related

Is there an insert method on a Rust Vec, which fills missing indexes with the default value?

I want a method which works like this:
let mut vec: Vec<i32> = Vec::new();
vec.<some_method>(3 /*index*/, 12 /*value*/);
// the indexes get filled with the default value for the type (i32)
// if the vec isn't long enough
assert_eq!(vec[..], [0, 0, 0, 12]);

Why are these two code samples equal? Difference between references, borrowed variables, pointers

fn largest(num_list: &[i32]) -> i32 {
let mut largest = num_list[0];
for &num in num_list {
if num > largest {
largest = num
}
}
largest
}
fn largest2(num_list: &[i32]) -> i32 {
let mut largest = num_list[0];
for num in num_list {
if num > &largest {
largest = *num
}
}
largest
}
fn main() {
let num_list = vec![30, 20, 10, 60, 50, 40];
let largest = largest(&num_list);
let largest2 = largest2(&num_list);
println!("The largest number in num_list fn is: {}.", largest);
println!("The largest number in num_list fn is: {}.", largest2);
}
As you can see there are some slight differences in largest and largest2. Can someone help me understand the differences here and why both samples of code actually function the same?
The main difference between the two samples stems from your loop statement, for num in num_list vs for &num in num_list. First of all, it is important to understand that they are equivalent to for [...] in num_list.iter(), that is, they iterate over references of elements of num_list. These references, of type &i32, are then assigned to either num or &num. In the first case, we just have a direct assignment, therefore num: &i32. In the second case, there is an irrefutable assignment that binds num to the number, therefore num: i32. Incidentally, this is possible because, even though you are trying to move out a value from a borrow, i32: Copy, so Rust compiles it fine.
The rest is just adaptation: either you are using a &i32, or directly a i32. For instance, when num: &i32, and you want to compare it with largest, you should dereference num and then compare it, giving *num > largest. However, num > &largest works too because Rust knows how to compare two &i32, by dereferencing both (so it will actually produce *num > largest). Similarly, when you assign to largest, you must assign an i32, so you dereference num: largest = *num.
It's easy to understand, then, why these two pieces of code do the same thing: in one version, you copy the value of a pointer to an integer in num, and then use it, whereas in the other you keep the dereference in num, and simply dereference it each time you need to use it.

How do I add the same integer to each element of a vector in Rust?

In Python, if I have a list and want to add 10 to each element I would do:
bar = [2,4,5,6,7]
bar = [x + 10 for x in bar]
resulting in: [12,14,15,16,17]. How can this be done in Rust? Is the only way doing a for loop and going through each vector element?
The Rust way to do this is very similar to Python: use iterators! The rough equivalent to Python's list comprehension is iter::map to get the new elements, and iter::collect to collect into a new vector (or some other kind of collection).
So for example, if bar is a Vec<i32> (or any other primitive integer type) and you want to add 10 to each element, try
bar = bar.into_iter().map(|x| x + 10).collect();
(playground)
Alternatively, you could mutate the elements in-place with
bar.iter_mut().for_each(|x| *x += 10);
(playground)
This is basically like a for loop, but a bit more succinct. This is generally going to be more efficient than the first method since you don't need to allocate a new vector (a sufficiently smart compiler may be able to avoid this). The only downside is that this version is less flexible. The output still needs to be a vector; you couldn't switch to a hash set or what have you. You also wouldn't be able to keep a copy of the old vector. See below for some examples of what's possible.
fn main() {
let mut bar = vec![2, 4, 5, 6, 7];
// Overwrite the old vector
bar = bar.into_iter().map(|x| x + 10).collect();
println!("new bar: {:?}", bar);
let bar = vec![2, 4, 5, 6, 7];
// Make a completely new vector
// Note that this works only because i32 implements the Copy trait,
// so we can make copies of the elements of bar without any problems
// In more general situations, we may need to clone each element
let foo: Vec<_> = bar.iter().map(|&x| x + 10).collect();
println!("old bar: {:?} (it's still around)", bar);
println!("new foo: {:?}", foo);
use std::collections::HashSet;
let bar = vec![2, 4, 5, 6, 7];
// transform the data and collect it into a HashSet
// instead of a vector
let bar: HashSet<_> = bar.into_iter().map(|x| x + 10).collect();
println!("new bar: {:?} (note that now bar is unordered)", bar);
let mut bar = vec![2, 4, 5, 6, 7];
// Overwrite the old vector in place
bar.iter_mut().for_each(|x| *x += 10);
println!("new bar: {:?}", bar);
}
(playground)
This is basic code which illustrates how to do it the way the question assumes by default. It might be useful for beginners with Rust like me:
fn increment_mut(p: &mut Vec<i32>, to_add: i32){
for i in 0..p.len() {
p[i] += to_add;
}
}
fn main() {
let mut p = vec![2, 4, 5, 6, 7];
increment_mut(&mut p, 10);
// Print the complete vector in Debug.
println!("{:?}", p)
}
$ cargo run
[12, 14, 15, 16, 17]
Using iter_mut
fn increment_mut2(p: &mut Vec<i32>, to_add: i32) {
for x in p.iter_mut() {
*x += to_add;
}
}
fn main() {
let mut p = vec![2, 4, 5, 6, 7];
increment_mut2(&mut p, 10);
// Print the complete vector in Debug.
println!("{:?}", p)
}
$ cargo run
[12, 14, 15, 16, 17]

Idiomatically parsing a whitespace-separated string into a Vec of tuples of differing types

I have a struct EnclosingObject which contains a field of a Vec of tuples. I want to implement FromStr for this struct in a way that an EnclosingObject can be parsed from a string with the following structure: <number of tuples> <tuple1 str1> <tuple1 str2> <tuple1 i32> <tuple2 str1> <tuple2 str2> ...
This is what I have come up with so far (ignoring the case of an invalid number of tuples):
use std::str::FromStr;
use std::num::ParseIntError;
#[derive(Debug)]
struct EnclosingObject{
tuples: Vec<(String, String, i32)>,
}
impl FromStr for EnclosingObject {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let elems_vec = s.split_whitespace().collect::<Vec<_>>();
let mut elems = elems_vec.as_slice();
let num_tuples = elems[0].parse::<usize>()?;
elems = &elems[1..];
let mut tuples = Vec::with_capacity(num_tuples);
for chunk in elems.chunks(3).take(num_tuples){
tuples.push((chunk[0].into(),
chunk[1].into(),
chunk[2].parse::<i32>()?));
}
Ok(EnclosingObject{
tuples : tuples
})
}
}
fn main(){
println!("{:?}", EnclosingObject::from_str("3 a b 42 c d 32 e f 50"));
}
(playground)
As expected, for a valid string it prints out:
Ok(EnclosingObject { tuples: [("a", "b", 42), ("c", "d", 32), ("e", "f", 50)] })
and for an invalid string e.g. "3 a b x c d 32 e f 50":
Err(ParseIntError { kind: InvalidDigit })
Can I parse this Vec of tuples in a more elegant/idiomatic way, such as by using iterators?
I tried a combination of map and collect, but the problem with this is the error handling:
let tuples = elems
.chunks(3)
.take(num_tuples)
.map(|chunk| (chunk[0].into(),
chunk[1].into(),
chunk[2].parse::<i32>()?))
.collect();
The questionmark-operator seems not to work in this context (within the tuple). So I transformed it a bit:
let tuples = try!(elems
.chunks(3)
.take(num_tuples)
.map(|chunk| {
let integer = chunk[2].parse::<i32>()?;
Ok((chunk[0].into(),
chunk[1].into(),
integer))})
.collect());
... which works, but again appears a bit cumbersome.
The questionmark-operator seems not to work in this context (within the tuple).
The problem is that ? returns an Err in case of failure and you weren't returning an Ok in case of success. The operator works just fine if you do that. Beyond that, you can avoid the extraneous allocation of the Vec by operating on the iterator from splitting on whitespace:
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut elems = s.split_whitespace();
let num_tuples = elems.next().expect("error handling: count missing").parse()?;
let tuples: Vec<_> = elems
.by_ref()
.tuples()
.map(|(a, b, c)| Ok((a.into(), b.into(), c.parse()?)))
.take(num_tuples)
.collect::<Result<_, _>>()?;
if tuples.len() != num_tuples { panic!("error handling: too few") }
if elems.next().is_some() { panic!("error handling: too many") }
Ok(EnclosingObject { tuples })
}
I've also used Itertools' tuples method which automatically groups an iterator into tuples and collected into a Result<Vec<_>, _>. I reduced the redundant tuples: tuples in the struct and added some placeholders for the remainder of the error handling. I removed the Vec::with_capacity because I trust that the size_hint set by take will be good enough. If you didn't trust it, you could still use with_capacity and then extend the vector with the iterator.

Alternative to Vec::chunks() that consumes the vector

I have the following function, which takes a vector as argument and returns a vector of its pairs of elements:
fn to_pairs(flat: Vec<u64>) -> Vec<(u64, u64)> {
assert!(flat.len() % 2 == 0);
let mut pairs = Vec::new();
pairs.reserve(flat.len() / 2);
for pair in flat.chunks(2) {
assert!(pair.len() == 2);
pairs.push((pair.get(0).unwrap().clone(), pair.get(1).unwrap().clone()));
}
pairs
}
I want consume the vector flat so I don't have to clone its elements when constructing the pair.
Is it possible to do so without reimplementing a variation of Vec::chunks() myself?
I want consume the vector flat so I don't have to clone its elements when constructing the pair.
Convert the input Vec into an iterator, then take two things from the iterator at a time. Essentially, you want the same thing as processing a Range (an iterator) in chunks:
fn to_pairs<T>(flat: Vec<T>) -> Vec<(T, T)> {
let len = flat.len();
assert!(len % 2 == 0);
let mut pairs = Vec::with_capacity(len / 2);
let mut input = flat.into_iter().peekable();
while input.peek().is_some() {
match (input.next(), input.next()) {
(Some(a), Some(b)) => pairs.push((a, b)),
_ => unreachable!("Cannot have an odd number of values"),
}
}
pairs
}
fn main() {
assert_eq!(vec![(1,2), (3,4)], to_pairs(vec![1,2,3,4]));
assert_eq!(vec![(true,true), (false,false)], to_pairs(vec![true,true,false,false]));
}
The assert!(len % 2 == 0); is quite important here, as Iterator makes no guarantees about what happens after the first time next returns None. Since we call next twice without checking the first value, we could be triggering that case. In other cases, you'd want to use fuse.
As pointed out by Kha, you could simplify the while loop a bit:
let mut input = flat.into_iter();
while let (Some(a), Some(b)) = (input.next(), input.next()) {
pairs.push((a, b));
}

Resources