Rustでは、変数に対する=式で変数の所有権が移動してしまう。従って、安易に=代入するとmoveが発生してしまいコンパイルできなくなる。
#[derive(Debug)] struct Color{ r:f32, g:f32, b:f32 } impl Color{ fn new(R:f32,G:f32,B:f32)->Color{ return Color{r:R,g:G,b:B}; } } fn main() { let color = Color::new(0.5,0.2,0.0); let ref1 = color; let ref2 = color; // ref1にcolorが入っているのでcolorは空。 println!("{:?}",color); }
これを避けるには参照(&T)を使うか、Rcという参照カウンタを持つスマートポインタを使う。C++でいうところのshared_ptr。
ここで重要な違いだが、
・Rc ... それぞれが所有権を持つ。すべてのRcが破棄された時が変数(の値)の寿命
・&T ... 参照は所有権を持たないので、有効範囲が参照元の変数の寿命に依存する。
Rc::new()でポインタを作成し、clone()で参照を複製する。
use std::rc::Rc; #[derive(Debug)] struct AB{ a:i32, b:i32, } ////////////////////////////////////////////////// fn main( ){ let ref3; { // Sharedポインタ作成 let myref=Rc::new(AB{a:5,b:10}); { // ポインタを複製。値の複製ではない let ab=myref.clone(); println!("{:?}",ab); // 参照カウント println!("Reference Count ab: {}",Rc::strong_count(&ab)); } // ref3は所有権を持つので、 // myrefのスコープが切れても値は破棄されない ref3 = myref.clone(); } println!("Reference Count ref3: {}",Rc::strong_count(&ref3)); // myrefは無効 // ref3 は有効 let k = ref3.a; println!("{}",k); }
Rustにはnullptrがない。Rcが無効であることを表現できないので、Optionと組み合わせる。
Optionは値があればSomeの中に入っている。値がなければNoneを持つ。
use std::{rc::Rc}; #[derive(Debug)] struct Color{ r:f32, g:f32, b:f32 } impl Color{ fn new(R:f32,G:f32,B:f32)->Color{ return Color{r:R,g:G,b:B}; } } ////////////////////////////////////////////////// fn main( ){ let my_color = Color::new(0.5, 0.4, 0.3); // nullptrができないのでOptionと組み合わせる let mut data: Option<Rc<Color>> = None; // Optionなので、Option::Someに値を入れる data = Some(Rc::new(my_color)); if let Some(color) = data{// dataが有効ならcolorを定義して代入 println!("{:?}",color); } else{ println!("nullptr"); } }
Rc::newにメモリ管理を移管するのは簡単だが、その逆はできないらしい。即ち、一度Rcで管理すると決めたら、もう自分で管理できなくなる。
use std::rc::Rc; #[derive(Debug)] struct AB{ a:i32, b:i32, } ////////////////////////////////////////////////// fn main( ){ let ab = AB{a:5,b:10}; let myref=Rc::new(ab);// abからmyrefにmoveするので、以後 ab は使えない println!("{:?}",myref);// ここで ab を使おうとするとビルドエラー }