電気ひつじ牧場

技術メモ

Rustにっき/6日目・参照2

6日目は参照の続きからです。

参照解決

CやC++などと同様、*で参照解決を行います。

let mut x = 10;
let m = &mut x;
*m += 4;
assert_eq!(*m, 14);

.演算子は暗黙的に借用や参照解決を行います。

.による借用の例

例としてベクタをソートしてみます。sort( )はhttps://doc.rust-lang.org/std/primitive.slice.html#method.sortを見ると&mut sliceをとるとあるので、それに合わせます。

let mut v = vec![2,3,1];
(&mut v).sort();

これは以下のように書いても同じ挙動をします。.によってvの借用が暗黙的に行われているからです。

let mut v = vec![2,3,1];
v.sort();

.による参照解決の例

人間型の山田さんを定義し、それを借用します。

struct Person {name: String, age: i32};
let p1 = Person {name: "yamada".to_string(), age: 21};
let p_ref = &p1;

以下の2つのコードは等価です。p_refが.により自動的に解決されます。`

assert_eq!((*p_ref).name, "yamada".to_string());
assert_eq!(p_ref.name, "yamada".to_string());

以下のようなprintln!マクロも内部的には.による参照解決を用いているようです。文字列リテラル(Stringではない)は&strを返すからですかね。

let x = "hello";
println!("{}", x);

参照の代入

C++は参照に対して代入すると、参照先に代入を行いますが、Rustはそうなりません。

let x = 20;
let y = 10;
let mut rx: &i32 = &x;
rx = &y;

普通にrxはyを指すようになります。ポインタっぽいですね。

参照をたどる

これすごいなって思いました。(小並感)

アドレスが違えど値まで辿って比較してくれます。アドレスが比較したい方はstd::ptr::eq(p1, p2)を使えば良いらしいです。

let x = 10;
let y = 10;
let rx = &x;
let ry = &y;
assert!(rx == ry);

安全性

Rustの本には生存期間の話が小難しく書いてありますが、要するに「無効になりうる参照を作成しようとすると、コンパイラがそれを検知してエラーを吐く」ことでメモリ安全が保たれています。

無効になりうる参照というのは、参照を解決した際に参照先が破棄されているような参照ことです。ぬるぽコンパイラが許さない。

関数のシグネチャ

i32型の参照を受け取るような関数fn myFunc(p: &i32)は、生存期間を明示すると、fn myFunc<'a>(p : &'a i32)のように書くことができます。

この'aは生存期間パラメータといい、myFuncの関数定義は「任意の生存期間'aをもつi32への参照」を引数に受け取るという意味になります。

'aの場合は省略できるようですが、引数の参照の生存期間がグローバルだと'staticなどと明示する必要があるようです。関数定義を見るだけで引数の参照がどれだけの生存期間を持つかが分かるので良いですね。

生存期間パラメータ

構造体が参照型をフィールドに持つ場合は生存期間パラメータを明示する必要があります。

struct S {
    r: &i32
}

書かないとエラーです。

error[E0106]: missing lifetime specifier
  --> src/main.rs:11:12
   |
11 |         r: &i32
   |            ^ expected lifetime parameter

このように修正します。

struct S<'a> {
    r: &'a i32
}

これでS型は参照型と同じく生存期間を持つようになります。S型の値を作ると生存期間'aはその部分を包含する必要があり、'aは参照rの生存期間に含まれていないといけません。(先にrに死なれるとダングリングポインタになって困る)

続く

生存期間という新しい概念が出てきてなかなかヘビーでした。生存期間周りの話はまだあまり理解していないので端折ります。また利用する機会があれば詳しく書くかも