rust
1struct S<'a> { y: &'a mut i32 } 2 3fn bar() { 4 let mut x: i32 = 1; 5 let s = S { y: &mut x }; 6 *s.y = 5; 7} 8 9fn baz() { 10 let mut x: i32 = 1; 11 let s = S { y: &mut x }; 12 let z = &s; 13 *z.y = 5; 14}
上のコード片で、 bar()
関数ではエラーは起きませんが、 baz()
関数では次のようなエラーが発生します。
error[E0389]: cannot assign to data in a `&` reference --> /home/dai/workspace/daily/2018/0222/junk00-11-41.rs:15:5 | 15 | *z.y = 5; | ^^^^^^^^ assignment into an immutable reference
ここでコンパイラは z.y
が immutable reference
であるとしています。
疑問は、
- どこを見て
z.y
をimmutable reference
としているのか
S::y
の型だけ見れば&mut i32
なのに。 - 参照経由で
*S::y
を書き換えるにはs
を mutable にするしかないのか
baz()
関数でもs
を mutable にしてz
を &mut 参照とすればエラーはなくなりました。
以下、1点目の、なぜ immutable reference
とされているかを理解するために考えたこと・試したことを記述します。少々長くなってしまいましたが、誤解している点などありましたら指摘していただけると嬉しいです。
以後、式 a
が型 T
であることを a : T
と書きます (ライフタイムは省略) 。a
が mutable であることを mut a
と書きます。
まず、 S
の定義では確かに y : &mut i32
と定義されています。*s.y
は y
そのものに対する変更 (y
が参照する先を変更するなど) ではなく y
の参照先を書き換える行為なので、 s
が mut s
である必要はないと考えました。実際 foo()
関数によりこれは確かめることができています。
*s.y = 5;
という処理で s
の所有権がそのまま絡むような操作はないと思っています。なので s
への参照 z
を経由しても同じことができてほしいです。かつ、 mut s
が要求されていないところでもし z : &mut S
が要求されるとすると違和感があるので、 z : &S
で通るはずだと考えます。ところが実際エラーが出ます。
適当に mut
を付けて次の qux()
関数のように書き換えると、コンパイルが通るようになりました。
rust
1fn qux() { 2 let mut x: i32 = 1; 3 let mut s = S { y: &mut x }; // mut s に 4 let z = &mut s; // &mut 参照に 5 *z.y = 5; 6}
つまり z : &mut S
が要求されています。そして s
を &mut 借用するために mut s
も要求されます。これはもしかして、 &mut な借用は同時に一つだけ存在するというルールに違反しないためにこのようになっているのでしょうか。確かに z : &S
経由で s.y : &mut i32
にアクセスできてしまうと、 s
の immutable な参照ならいくつでも作れてしまうので、 S::y
という &mut な参照が複数作成できることになります。
もし S::y : &mut i32
が、 a : S
または a : &mut S
のときのみ S::y : &mut i32
で、 a : &S
のときは S::y : &i32
となると見做されるなら、確かに先のように複数作成できてしまうということはなくなります。
この仕組みを考えていたところで、次の関数 quux()
を試してみました。
rust
1fn quux() { 2 let mut x: i32 = 1; 3 let mut s = S { y: &mut x }; 4 let z = &mut s; 5 *s.y = 5; // z -> s に変更 6}
すると、エラーが発生して :
error[E0506]: cannot assign to `*s.y` because it is borrowed --> /home/dai/workspace/daily/2018/0222/junk00-11-41.rs:29:5 | 28 | let z = &mut s; | - borrow of `*s.y` occurs here 29 | *s.y = 5; | ^^^^^^^^ assignment to borrowed `*s.y` occurs here
...つまり、エラーによると &mut s
(&s
も同様に) は s.y
のみならず *s.y
まで借用しているということになると思いますが、その認識で正しいでしょうか。それならば &s
は *s.y
を &*s.y
のような形で借用し、&mut s
は *s.y
を &mut *s.y
のような形で借用しているということなのでしょうか。
先の
もし
S::y : &mut i32
が、a : S
またはa : &mut S
のときのみS::y : &mut i32
で、a : &S
のときはS::y : &i32
となると見做される
仕組みはこういうことなのでしょうか。
非常に長くなりましたが、よろしくお願いします。

回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/02/22 06:21
2018/02/22 07:15
2018/02/22 08:01