よく分からないので調べたことをまとめた。
所有権の厄介さ
i32 型のベクタオブジェクト v1、ベクタオブジェクト v2 の要素の総和を返す関数を例とする。
fn main() {
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let sum = sum_vector(v1, v2);
println!("v1 and v2 sum is {}", sum);// <-- value moved here
println!("v1 is {:?} ", v1); // <-- Error!!! value used here after move
println!("v2 is {:?} ", v2); // <-- Error!!! value used here after move
}
fn sum_vector(v1: Vec<i32>, v2: Vec<i32>) -> i32 {
let v1_sum = v1.iter().fold(0, |sum, x| sum + x);
let v2_sum = v2.iter().fold(0, |sum, x| sum + x);
v1_sum + v2_sum
}
ベクタオブジェクト v1 はsum_vector
関数のスコープ内に所有権が移動しているため、sum_vector
関数のスコープから外れた後に、呼び出し元のmain
関数内で参照するとエラーが出力される。v2 も同様である。
再度main
関数内で使用するなら、sum_vector
関数の戻り値にベクタオブジェクト v1 及び v2 を含める必要がある。
以下の例ではタプルに含めてで返すようにsum_vector
関数を変更した。
fn main() {
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let (v1, v2, sum) = sum_vector(v1, v2);
println!("sum is {}", sum); // v1 and v2 sum is 21
println!("v1 is {:?} ", v1); // v1 is [1, 2, 3]
println!("v2 is {:?} ", v2); // v2 is [4, 5, 6]
}
fn sum_vector(v1: Vec<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, i32) {
let v1_sum = v1.iter().fold(0, |sum, x| sum + x);
let v2_sum = v2.iter().fold(0, |sum, x| sum + x);
(v1, v2, v1_sum + v2_sum)
}
これは、嫌だ。
借用
以下のコードに書き換えることで、所有権の借用を行うことができる。
fn main() {
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let sum = sum_vector(&v1, &v2);
println!("sum is {}", sum);
println!("v1 is {:?} ", v1);
println!("v2 is {:?} ", v2);
}
fn sum_vector(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 {
let v1_sum = v1.iter().fold(0, |sum, x| sum + x);
let v2_sum = v2.iter().fold(0, |sum, x| sum + x);
v1_sum + v2_sum
}
- sum_vector 関数に渡す変数を
v1, v2
から&v1, &v2
に変更。宣言自体はv1, v2
のまま。 - 引数の型を
Vec<i32>
->&Vec<i32>
&T 型は参照と呼ばれ、リソースの所有権を借用する。
参照の種類
通常の参照
&T 型はイミュータブルなので変更できない。下記の例だとコンパイルエラーになる。
fn main() {
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let sum = sum_vector(&v1, &v2);
...
}
fn sum_vector(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 {
v1.push(5); // cannot borrow as mutable
...
}
&mut 参照
&mut T 型だと参照しているリソースを変更できる。これを前者を参照と呼ぶのと区別し、&mut参照
と呼ぶ
fn main() {
let mut v1 = vec![1, 2, 3];
let mut v2 = vec![4, 5, 6];
let sum = sum_vector(&mut v1, &mut v2);
...
}
fn sum_vector(v1: &mut Vec<i32>, v2: &mut Vec<i32>) -> i32 {
v1.push(5);
...
}
&mut 参照は同じスコープでは行えない。下記の例ではコンパイルエラーになる。
fn same_scope(){
let mut v1 = vec![1, 2, 3];
let y1 = &mut v1; // v1の&mut参照開始
y1.push(6);
println!("v1 is {:?}", v1); // ここでv1を借用しようとするため、エラーになる
// => cannot borrow `v1` as immutable because it is also borrowed as mutable
}
不正な参照
以下の例だと、スコープの終わりで変数 x が解放されるため、参照が不正となり、コンパイルエラーになる。
let y: &Vec<i32>;
{
let x = vec![1, 2, 3];
y = &x; // error[E0597]: `x` does not live long enough
}
println!("y is {:?}", y);
同じスコープでも、参照する変数が先に宣言されているとコンパイルエラーとなる。
これは後に宣言した変数から順に解放されるためである。下記の例だと変数 x -> y の順に解放されるので、y の参照が不正になる。
let y: &Vec<i32>;
let x = vec![1, 2, 3];
y = &x; // error[E0597]: `x` does not live long enough
println!("y is {:?}", y);
つまりこれなら大丈夫。
let x = vec![1, 2, 3];
let y: &Vec<i32>;
y = &x;
println!("y is {:?}", y);// => y is [1, 2, 3]