Rustでは、値を変更できる参照は一度に一つしか作れない。
struct structAB { v:i32, } fn main() { let mut oo=structAB{v:5}; let _r:&mut structAB = &mut oo; // OK let _g:&mut structAB = &mut oo; // NG 可変の参照。二つ目以降は作れない let _b:&mut structAB = &mut oo; // NG _r.v=2; _g.v=1; // NG そもそも定義できない _b.v=0; // NG そもそも定義できない }
immutableな参照であればいくつでも作れる。
struct structAB { v:i32, } fn main() { let mut oo=structAB{v:5}; let _r:&structAB = &oo; // OK let _g:&structAB = &oo; // OK &mut でなければいくつでも作れる let _b:&structAB = &oo; // OK let r = _r.v; // OK 読み取りはできる let g = _g.v; // ただし 書き換えられない。 let b = _b.v; // }
Rustの参照は制限がきつく間違った使い方をコンパイラがはじいてくれるweak_ptrと考えるとC++erにとってはわかりやすいかもしれない。
#include <iostream> #include <memory> struct StructAB { int v; StructAB(int a) :v(a) {} }; int main() { auto oo = std::make_shared<StructAB>(5); // C++ では所有権を持たない参照をいくつでも参照を作れる std::weak_ptr<StructAB> _r = oo; std::weak_ptr<StructAB> _g = oo; // Rustではエラー std::weak_ptr<StructAB> _b = oo; // 二つ目以降の可変な参照を作れない _r.lock()->v = 2; _g.lock()->v = 1; // Rustではエラー _b.lock()->v = 0; // Rustではエラー }
借用はC++でいうところのmove。問題は、Rustの場合、(プリミティブ型以外)=代入の挙動がデフォルトでmoveになっていることで、「operator=は常にコピーであることが望ましい」というC++の哲学とは全く相いれない。
struct structAB { v:i32, } fn main() { let oo=structAB{v:5}; let pp = oo; // ただの=がmoveになる println!("{} => {}",oo.v , pp.v);// value borrowed here after move (ooが無効) }
#include <iostream> #include <memory> struct StructAB { int v; StructAB(int a) :v(a) {} }; int main() { auto oo = std::make_unique<StructAB>(5); auto pp = std::move(oo); // ここでmoveすると以降ooはnullptr printf("%d => %d\n", oo->v,pp->v); // ランタイムエラー (ooがnullptr) }
それどころか、引数渡しですらデフォルトでmoveなので、迂闊に渡すとライフタイムが切れてしまう。
struct structAB { v:i32, }
fn test(ii:structAB)->i32{ return ii.v*2; // ii はここで解放される }
fn main() { let oo=structAB{v:5}; let pp = test(oo); // ここで oo がmoveされてしまうので、test関数から抜けた段階でライフタイムが切れる println!("{} => {}",oo.v , pp); }
#include <iostream> #include <memory> struct StructAB { int v; StructAB(int a) :v(a) {} };
int test(std::unique_ptr<StructAB> ii) { return ii->v * 2; // ii はここでデストラクトされる }
int main() { auto oo = std::make_unique<StructAB>(5); auto pp = test(std::move(oo)); // ここでmoveすると以降ooはnullptr printf("%d => %d\n", oo->v,pp); }