How to reassign a higher-scoped reference variable in a loop - rust

Suppose I have a modify_vec() function that takes a vector reference and returns a new vector. It doesn't really matter what it does so for the sake of example it will just append a given number.
fn modify_vec(v: &Vec<i64>, i: i64) -> Vec<i64> {
let mut new_v = v.clone();
new_v.push(i);
new_v
}
Now, suppose I want to run this modify_vec a few times, I could do it like that:
fn main() {
let v = vec![1, 2, 3];
println!("{:?}", run(&v)); // Outputs [1, 2, 3, 4, 5, 6]
}
fn run(v: &Vec<i64>) -> Vec<i64> {
let new_1 = modify_vec(&v, 4);
let new_2 = modify_vec(&new_1, 5);
modify_vec(&new_2, 6)
}
Or I can do it like that
fn run(v: &Vec<i64>) -> Vec<i64> {
let mut new_v = modify_vec(&v, 4);
new_v = modify_vec(&new_v, 5);
modify_vec(&new_v, 6)
}
However it would obviously be better to use a loop. The problem is that the run function starts with a reference of the vector, but it must declare a new owned variable to store the returned value of modify_vec. The only way I found to initialize that owned variable outside of the loop was doing a .clone(), like this.
fn run(v: &Vec<i64>) -> Vec<i64> {
let mut new_v = v.clone();
for i in 4..7 {
new_v = modify_vec(&new_v, i);
}
new_v
}
But this seems "wasteful", as it is my understanding that it makes a deep copy. In the previous unlooped examples, it was not necessary to clone the vector prior to passing it to the first call to modify_vec.Is there a better way?

modify_vec should take a Vec by value instead of a reference. Let the caller clone the Vec if it doesn't want to (or cannot) transfer ownership – do not impose the cost of a clone on all callers.
fn main() {
let v = vec![1, 2, 3];
println!("{:?}", run(&v)); // Outputs [1, 2, 3, 4, 5, 6]
}
fn run(v: &Vec<i64>) -> Vec<i64> {
let mut new_v = v.clone();
for i in 4..7 {
new_v = modify_vec(new_v, i);
}
new_v
}
fn modify_vec(mut v: Vec<i64>, i: i64) -> Vec<i64> {
v.push(i);
v
}
Perhaps the example is too contrived, but there is no need to clone any Vec at all:
fn main() {
let v = vec![1, 2, 3];
println!("{:?}", run(v)); // Outputs [1, 2, 3, 4, 5, 6]
// ^ this could be replaced with `v.clone()`
// if `v` needs to be used later
}
fn run(mut v: Vec<i64>) -> Vec<i64> {
for i in 4..7 {
v = modify_vec(v, i);
}
v
}
fn modify_vec(mut v: Vec<i64>, i: i64) -> Vec<i64> {
v.push(i);
v
}

Related

Rust iterate over borrowed mutable reference

I wanted a function to control and change a vector in rust. Here is a simplified version:
fn foo(vec: &mut Vec<i32>) {
for (i, element) in vec.iter().enumerate() {
// Some checks here
vec[(*element) as usize] = i as i32;
}
}
fn main() {
let mut bar: Vec<i32> = vec![1, 0, 2];
foo(&mut bar);
}
This code does not compile because there is both an immutable and a mutable borrow of vec in foo. I tried getting around this by copying vec to a separate copy, which didn't work and also wouldn't have been very pretty. What is the correct way to do this?
If you want to mutate the Vec, the correct way is to iterate over it mutably instead of immutably:
fn foo(vec: &mut Vec<i32>) {
// note the `iter_mut` here:
for element in vec.iter_mut() {
// Some checks here
// element now has type `&mut i32` and we can mutate it directly.
*element *= 2;
}
}
fn main() {
let mut bar: Vec<i32> = vec![1, 2, 3];
foo(&mut bar);
println!("{:?}", bar); // [2, 4, 6]
}
You can avoid borrowing the whole Vec by using index access like this:
fn foo(vec: &mut Vec<i32>) {
for index in 0..vec.len() {
let element = vec[index];
if element <= 0 {
continue;
}
vec[index] = index as i32;
}
}
fn main() {
let mut bar: Vec<i32> = vec![1, 0, 2];
foo(&mut bar);
println!("{:?}", bar)
}

How to get mutable slice with conditions in Rust

I'd like to write something similar with following code in Rust.
fn main() {
let mut v: Vec<i64> = vec![1, 2, 3, 4, 5];
let mut s: Vec<&mut i64> = v
.iter_mut()
.filter(|val| **val < 2_i64)
.collect();
if s.len() == 0 {
s = v
.iter_mut()
.filter(|val| **val > 2_i64)
.collect();
}
*s[0] = 0;
println!("{:?}", v);
}
It's obviously making borrowing reference twice. I know it causes an error, E0384: cannot assign twice to immutable variable s cannot assign twice to immutable variable.
Could you tell me how to write this kind of work flow in Rust? I want to filter value and if it returns nothing, apply another filter and get Vec of borrowing reference.
I tried to use shared reference. After filtered the Vec, I needed to convert shared reference to borrowing one, but it was not possible.
You can write something like that. I agree that it's not really beautiful, but it works:
fn main() {
let mut v = vec![1_i64, 2, 3, 4, 5];
let s: Vec<_> = v.iter_mut().filter(|&val| *val < 2).collect();
let mut s = if s.len() == 0 {
v.iter_mut().filter(|&val| *val > 2).collect()
} else {
s
};
*s[0] = 0;
println!("{:?}", v);
}
We can clone the reference like this:
fn main() {
let mut v: Vec<i64> = vec![1, 2, 3, 4, 5];
let mut t = v.clone();
let mut s: Vec<&mut i64> = v.iter_mut().filter(|val| **val < 2_i64).collect();
if s.len() == 0 {
s = t.iter_mut().filter(|val| **val > 2_i64).collect();
}
*s[0] = 0;
println!("{:?}", v);
}

How do I sort an array?

Why cannot I not sort an array as expected?
fn main() {
let mut a = [1,3,2];
let s = a.sort();
println!("{:?}", s);
}
a is sorted, but the method sorts the array in place. Read the signature of sort: sort takes &mut self and returns unit (i.e. nothing), so when you print s, you print ().
Working code:
fn main() {
let mut a = [1, 3, 2];
a.sort();
assert_eq!(a, [1, 2, 3]);
println!("{:?}", a);
}
Writing a function that returns a sorted array
You can write a function that does what you want:
fn sort<A, T>(mut array: A) -> A
where
A: AsMut<[T]>,
T: Ord,
{
let slice = array.as_mut();
slice.sort();
array
}
fn main() {
let a = [1, 3, 2];
assert_eq!(sort(a), [1, 2, 3]);
}

How do I create a Rust function that is generic in the operation applied?

I have a function called new_vec. It takes two vectors and creates a new one, by performing an elementwise operation on the pair of elements from the zipped vectors.
fn main() {
let v1s = vec![1, 0, 1];
let v2s = vec![0, 1, 1];
let v3s = new_vec(v1s, v2s);
println!("{:?}", v3s) // [1, 1, 2]
}
fn new_vec(v1s: Vec<i32>, v2s: Vec<i32>) -> Vec<i32> {
let mut v3s = Vec::<i32>::new();
for (v1, v2) in v1s.iter().zip(v2s.iter()) {
v3s.push(v1 + v2) // would also like to use -
}
v3s
}
I want to have a new_vec function for the common binary operation that is possible to use on two integers, such as +, -, /, *.
How do I do this? I can imagine two ways: macros and closures. A minimal example of how to do this in the best way, for example with + and - would be appreciated.
I would pass a closure:
fn new_vec<F>(v1s: &[i32], v2s: &[i32], foo: F) -> Vec<i32>
where F: Fn(i32, i32) -> i32
{
let mut v3s = Vec::<i32>::new();
for (&v1, &v2) in v1s.iter().zip(v2s.iter()) {
v3s.push(foo(v1, v2))
}
v3s
}
fn main() {
let v1s = vec![1, 0, 1];
let v2s = vec![0, 1, 1];
let v3s = new_vec(&v1s, &v2s, |x, y| x - y);
let v4s = new_vec(&v1s, &v2s, |x, y| x + y);
println!("{:?}", v3s); // [1, -1, 0]
println!("{:?}", v4s); // [1, 1, 2]
}
Note the change in the first two parameters; if your function doesn't need to consume its arguments, references are preferable to Vectors - in this case &[i32].
This implementation is not too efficient because the resulting Vector is extended incrementally; it's better if you modified it as follows to reduce the number of allocations:
fn new_vec<F>(v1s: &[i32], v2s: &[i32], foo: F) -> Vec<i32>
where F: Fn(i32, i32) -> i32
{
v1s.iter().zip(v2s.iter()).map(|(&x, &y)| foo(x, y)).collect()
}

Efficiently insert or replace multiple elements in the middle or at the beginning of a Vec?

Is there any straightforward way to insert or replace multiple elements from &[T] and/or Vec<T> in the middle or at the beginning of a Vec in linear time?
I could only find std::vec::Vec::insert, but that's only for inserting a single element in O(n) time, so I obviously cannot call that in a loop.
I could do a split_off at that index, extend the new elements into the left half of the split, and then extend the second half into the first, but is there a better way?
As of Rust 1.21.0, Vec::splice is available and allows inserting at any point, including fully prepending:
let mut vec = vec![1, 5];
let slice = &[2, 3, 4];
vec.splice(1..1, slice.iter().cloned());
println!("{:?}", vec); // [1, 2, 3, 4, 5]
The docs state:
Note 4: This is optimal if:
The tail (elements in the vector after range) is empty
or replace_with yields fewer elements than range’s length
or the lower bound of its size_hint() is exact.
In this case, the lower bound of the slice's iterator should be exact, so it should perform one memory move.
splice is a bit more powerful in that it allows you to remove a range of values (the first argument), insert new values (the second argument), and optionally get the old values (the result of the call).
Replacing a set of items
let mut vec = vec![0, 1, 5];
let slice = &[2, 3, 4];
vec.splice(..2, slice.iter().cloned());
println!("{:?}", vec); // [2, 3, 4, 5]
Getting the previous values
let mut vec = vec![0, 1, 2, 3, 4];
let slice = &[9, 8, 7];
let old: Vec<_> = vec.splice(3.., slice.iter().cloned()).collect();
println!("{:?}", vec); // [0, 1, 2, 9, 8, 7]
println!("{:?}", old); // [3, 4]
Okay, there is no appropriate method in Vec interface (as I can see). But we can always implement the same thing ourselves.
memmove
When T is Copy, probably the most obvious way is to move the memory, like this:
fn push_all_at<T>(v: &mut Vec<T>, offset: usize, s: &[T]) where T: Copy {
match (v.len(), s.len()) {
(_, 0) => (),
(current_len, _) => {
v.reserve_exact(s.len());
unsafe {
v.set_len(current_len + s.len());
let to_move = current_len - offset;
let src = v.as_mut_ptr().offset(offset as isize);
if to_move > 0 {
let dst = src.offset(s.len() as isize);
std::ptr::copy_memory(dst, src, to_move);
}
std::ptr::copy_nonoverlapping_memory(src, s.as_ptr(), s.len());
}
},
}
}
shuffle
If T is not copy, but it implements Clone, we can append given slice to the end of the Vec, and move it to the required position using swaps in linear time:
fn push_all_at<T>(v: &mut Vec<T>, mut offset: usize, s: &[T]) where T: Clone + Default {
match (v.len(), s.len()) {
(_, 0) => (),
(0, _) => { v.push_all(s); },
(_, _) => {
assert!(offset <= v.len());
let pad = s.len() - ((v.len() - offset) % s.len());
v.extend(repeat(Default::default()).take(pad));
v.push_all(s);
let total = v.len();
while total - offset >= s.len() {
for i in 0 .. s.len() { v.swap(offset + i, total - s.len() + i); }
offset += s.len();
}
v.truncate(total - pad);
},
}
}
iterators concat
Maybe the best choice will be to not modify Vec at all. For example, if you are going to access the result via iterator, we can just build iterators chain from our chunks:
let v: &[usize] = &[0, 1, 2];
let s: &[usize] = &[3, 4, 5, 6];
let offset = 2;
let chain = v.iter().take(offset).chain(s.iter()).chain(v.iter().skip(offset));
let result: Vec<_> = chain.collect();
println!("Result: {:?}", result);
I was trying to prepend to a vector in rust and found this closed question that was linked here, (despite this question being both prepend and insert AND efficiency. I think my answer would be better as an answer for that other, more precises question because I can't attest to the efficiency), but the following code helped me prepend, (and the opposite.) [I'm sure that the other two answers are more efficient, but the way that I learn, I like having answers that can be cut-n-pasted with examples that demonstrate an application of the answer.]
pub trait Unshift<T> { fn unshift(&mut self, s: &[T]) -> (); }
pub trait UnshiftVec<T> { fn unshift_vec(&mut self, s: Vec<T>) -> (); }
pub trait UnshiftMemoryHog<T> { fn unshift_memory_hog(&mut self, s: Vec<T>) -> (); }
pub trait Shift<T> { fn shift(&mut self) -> (); }
pub trait ShiftN<T> { fn shift_n(&mut self, s: usize) -> (); }
impl<T: std::clone::Clone> ShiftN<T> for Vec<T> {
fn shift_n(&mut self, s: usize) -> ()
// where
// T: std::clone::Clone,
{
self.drain(0..s);
}
}
impl<T: std::clone::Clone> Shift<T> for Vec<T> {
fn shift(&mut self) -> ()
// where
// T: std::clone::Clone,
{
self.drain(0..1);
}
}
impl<T: std::clone::Clone> Unshift<T> for Vec<T> {
fn unshift(&mut self, s: &[T]) -> ()
// where
// T: std::clone::Clone,
{
self.splice(0..0, s.to_vec());
}
}
impl<T: std::clone::Clone> UnshiftVec<T> for Vec<T> {
fn unshift_vec(&mut self, s: Vec<T>) -> ()
where
T: std::clone::Clone,
{
self.splice(0..0, s);
}
}
impl<T: std::clone::Clone> UnshiftMemoryHog<T> for Vec<T> {
fn unshift_memory_hog(&mut self, s: Vec<T>) -> ()
where
T: std::clone::Clone,
{
let mut tmp: Vec<_> = s.to_owned();
//let mut tmp: Vec<_> = s.clone(); // this also works for some data types
/*
let local_s: Vec<_> = self.clone(); // explicit clone()
tmp.extend(local_s); // to vec is possible
*/
tmp.extend(self.clone());
*self = tmp;
//*self = (*tmp).to_vec(); // Just because it compiles, doesn't make it right.
}
}
// this works for: v = unshift(v, &vec![8]);
// (If you don't want to impl Unshift for Vec<T>)
#[allow(dead_code)]
fn unshift_fn<T>(v: Vec<T>, s: &[T]) -> Vec<T>
where
T: Clone,
{
// create a mutable vec and fill it
// with a clone of the array that we want
// at the start of the vec.
let mut tmp: Vec<_> = s.to_owned();
// then we add the existing vector to the end
// of the temporary vector.
tmp.extend(v);
// return the tmp vec that is identitcal
// to unshift-ing the original vec.
tmp
}
/*
N.B. It is sometimes (often?) more memory efficient to reverse
the vector and use push/pop, rather than splice/drain;
Especially if you create your vectors in "stack order" to begin with.
*/
fn main() {
let mut v: Vec<usize> = vec![1, 2, 3];
println!("Before push:\t {:?}", v);
v.push(0);
println!("After push:\t {:?}", v);
v.pop();
println!("popped:\t\t {:?}", v);
v.drain(0..1);
println!("drain(0..1)\t {:?}", v);
/*
// We could use a function
let c = v.clone();
v = unshift_fn(c, &vec![0]);
*/
v.splice(0..0, vec![0]);
println!("splice(0..0, vec![0]) {:?}", v);
v.shift_n(1);
println!("shift\t\t {:?}", v);
v.unshift_memory_hog(vec![8, 16, 31, 1]);
println!("MEMORY guzzler unshift {:?}", v);
//v.drain(0..3);
v.drain(0..=2);
println!("back to the start: {:?}", v);
v.unshift_vec(vec![0]);
println!("zerothed with unshift: {:?}", v);
let mut w = vec![4, 5, 6];
/*
let prepend_this = &[1, 2, 3];
w.unshift_vec(prepend_this.to_vec());
*/
w.unshift(&[1, 2, 3]);
assert_eq!(&w, &[1, 2, 3, 4, 5, 6]);
println!("{:?} == {:?}", &w, &[1, 2, 3, 4, 5, 6]);
}

Resources