#vector #rust
Вопрос:
Каков хороший способ инициализации вектора без mut
ключевого слова, учитывая, что вектор будет неизменным после инициализации?
Например:
// 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, amp;num) in nums.iter().enumerate(){
padded[idx 1] = num;
}
// `padded` will be read-only/immutable ever since
В противном случае, чтобы отметить padded
mut
, просто инициализировать его, мне кажется пустой тратой неизменности, так как я не могу применить его после инициализации.
Ответ №1:
Распространенная идиома, встречающаяся в Rust, заключается в том, чтобы ввести локальную область действия именно для этой цели.
let padded: Vec<i32> = {
let mut tmp: Vec<i32> = vec![0; len_p];
tmp[0] = 1;
tmp[len_n 1] = 1;
for (idx, amp;num) in nums.iter().enumerate(){
tmp[idx 1] = num;
}
tmp
};
Внутри вложенной области у нас есть локальная переменная tmp
, которая называется изменяемой. Затем, когда мы доберемся до конца этой области, мы передадим право собственности на этот вектор неизменяемой переменной padded
. Компилятор может (и, скорее всего, будет) оптимизировать любое фактическое движение, которое происходит, и это скомпилируется до чего-то столь же эффективного, как то, что вы написали. Но с точки зрения проверки заимствований, tmp
она изменчива и padded
неизменна, как и требуется. Как только tmp
выходит за рамки, больше нет возможности изменять padded
.
Помните, что владелец значения всегда определяет, является ли это значение изменяемым или нет, поэтому, когда вы передаете право собственности кому-то другому, он может свободно изменять изменяемость значения при вступлении во владение.
Комментарии:
1. Если
padded
предполагается, что он остается неизменным навсегда (т. Е. Не перемещается, а затем мутирует в другом месте), также может быть хорошей идеей сделать его типомBox<[i32]>
, вызвавtmp.into_boxed_slice()
в конце области.
Ответ №2:
Есть 3 способа:
- Наиболее функциональный (FP) подход: Используйте сбор
let v: Vec<i32> = std::iter::once(1).chain(
nums.iter().copied()
)
.collect();
- Инициализировать в области действия. Полезно, когда вам нужны некоторые дополнительные переменные, которые будут очищены после этого.
let v = {
let mut v: Vec<i32> = vec![0; len_p];
v[0] = 1;
v[len_n 1] = 1;
for (amp;src, dst) in nums.iter().zip(v[1..].iter_mut()){
*dst = src;
}
v
}
- Свяжите имя заново и переместите в него вектор. Полезно, когда инициализация ясна и проста.
let mut v: Vec<i32> = vec![0; len_p];
v[0] = 1;
v[len_n 1] = 1;
for (amp;src, dst) in nums.iter().zip(v[1..].iter_mut()){
*dst = src;
}
// Rebind to immutable variable
let v = v;
// Cannot mutate here