Leon
Leon

Reputation: 901

How to properly initialize a vector in Rust, given that the vector will be immutable after the initialization?

What is a good way to initialize the vector, without the mut keyword, given that the vector will be immutable after the initialization?

For example:

// nums is a `i32` vector(Vec<i32>)
// here is to pad two 1s with both ends
let mut padded: Vec<i32> = vec![0; len_p];
padded[0] = 1;
padded[len_n + 1] = 1;
for (idx, &num) in nums.iter().enumerate(){
    padded[idx + 1] = num;
}
// `padded` will be read-only/immutable ever since

Otherwise, to mark padded mut, simply to initialize it, seems to me a waste of immutability, since I cannot enforce it after the initialization.

Upvotes: 2

Views: 902

Answers (2)

Angelicos Phosphoros
Angelicos Phosphoros

Reputation: 3057

There are 3 ways:

  1. Most functional (FP) approach: Use collect
let v: Vec<i32> = std::iter::once(1).chain(
      nums.iter().copied()
   )
   .collect();
  1. Initialize in scope. Useful when you need some extra variables which would be cleaned up after this.
let v = {
  let mut v: Vec<i32> = vec![0; len_p];
  v[0] = 1;
  v[len_n + 1] = 1;
  for (&src, dst) in nums.iter().zip(v[1..].iter_mut()){
     *dst = src;
  }
  v
}
  1. Rebind name and move vector into it. Useful when initialization is clear and simple.
  let mut v: Vec<i32> = vec![0; len_p];
  v[0] = 1;
  v[len_n + 1] = 1;
  for (&src, dst) in nums.iter().zip(v[1..].iter_mut()){
     *dst = src;
  }
  // Rebind to immutable variable
  let v = v;
  // Cannot mutate here

Upvotes: 3

Silvio Mayolo
Silvio Mayolo

Reputation: 70277

A common idiom seen in Rust is to introduce a local scope for just this purpose.

let padded: Vec<i32> = {
  let mut tmp: Vec<i32> = vec![0; len_p];
  tmp[0] = 1;
  tmp[len_n + 1] = 1;
  for (idx, &num) in nums.iter().enumerate(){
    tmp[idx + 1] = num;
  }
  tmp
};

Inside the nested scope, we have a local variable called tmp which is mutable. Then, when we get to the end of that scope, we pass ownership of that vector to the immutable variable padded. The compiler is free to (and likely will) optimize out any actual movement that's happening, and this will compile to something as efficient as what you wrote. But from the borrow checker's perspective, tmp is mutable and padded is immutable, as desired. Once tmp goes out of scope, there's no way to modify padded anymore.

Remember, the owner of a value always determines whether or not that value is mutable, so when you pass ownership to someone else, they're free to change the mutability of the value upon taking ownership.

Upvotes: 6

Related Questions