The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
Rust 歴 4ヶ月ぐらい
OSS で regtail を開発中
https://github.com/StoneDot/regtail
某ソシャゲ会社でサーバーエンジニア
仕事ではPHPを使っている
@StoneDot
5.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
Future, Generator のためには自己参照する構造体を扱いたい
しかし、自己参照する構造体は Move すると不正な状態に陥る
value pointer
元居た領域 value pointer
ポインタが古い領域を示してしまう
Move
7.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
大きさ2の配列を受け取り、いずれかの要素を選択する構造体
構造体作成時には0番目の要素を選択
toggle を使って選択要素を切り替え
get, set で値の取得と変更ができる
impl SelfRef {
pub fn new(ary: [i32; 2]) -> Pin<Box<SelfRef>> { ... }
pub fn toggle(self) { ... }
pub fn get(self) -> i32 { ... }
pub fn set(self, value: i32) { ... }
}
8.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
関数をまたがなければ参照で安全にできる(つまり使いものにならない)
参照における制約(1 mutable borrow)は引き継ぐ(Read Only ならば OK)
let mut ary = [1, 2];
let mut data = SelfRef { ary, ptr: &ary[0] };
data.ptr = &data.ary[0];
data.ptr = &data.ary[1];
#[derive(Debug)]
struct SelfRef<'a> {
ary: [i32; 2],
ptr: &'a i32,
}
*data.ptr = 10; こういった値の書き換えはできない
9.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
関数またいで使えないとかイケてないし、参照先も書き換えたい
Pointer 使えば良いじゃん!
unsafe だけどこれは流石に安全だよね?
#[derive(Debug)]
struct SelfRef {
ary: [i32; 2],
ptr: NonNull<i32>,
}
impl SelfRef {
fn get(self: &Self) -> i32 {
unsafe {
*self.ptr.as_ref()
}
}
}
10.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
全然安全ではありません! Moveした瞬間死にます
-919602312 コンストラクタで move するので即死
impl SelfRef {
fn new() -> SelfRef {
let mut data = SelfRef { ary: [1, 2], ptr: NonNull::dangling() };
data.ptr = NonNull::from(&data.ary[0]);
data
}
}
let mut data = SelfRef::new();
println!("{:?}", data.get());
11.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
BOX化すればアドレスは基本変わらないので、安全なはず!
impl SelfRef {
fn new(ary: [i32; 2]) -> Box<SelfRef> {
let data = SelfRef { ary, ptr: NonNull::dangling() };
let mut boxed = Box::new(data);
boxed.ptr = NonNull::from(&boxed.ary[0]);
boxed
}
}
struct SelfRef {
ary: [i32; 2],
pub ptr: NonNull<i32>,
}
デバッグのためにつけた
12.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
std::mem::swap, std::mem::replace みたいなことをされると死ぬ
let mut data1 = SelfRef::new([1, 2]);
let mut data2 = SelfRef::new([3, 4]);
std::mem::swap(data1.as_mut(), data2.as_mut());
unsafe { *data1.ptr.as_mut() = 100; }
println!("{} {}", data1.ary[0], data1.ary[1]);
println!("{} {}", data2.ary[0], data2.ary[1]);
3 4
100 2
data1 を書き換えたつもりが、data2 が変わっている
100 4
1 2
本当は みたいになって欲しい
13.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
参照を使うとデータの中身を変化させられない&関数跨げない
1 mutable borrow の制約からの自然な帰結
関数を跨げないので基本的に使い物にならない
NonNull<T>で自己参照できるけど……
Moveされるだけで簡単にダングリングポインタになっちゃう
Box化すればヒープにいるから基本大丈夫だけど、std::mem::swapとかで死ぬ
&mut が取り出せちゃうと move, swap などを止めることが出来ないのが最大の原因
Move禁止を強制したいけどどうすればいいんだ!
15.
The Rust logois licensed under CC BY 4.0 (This is unofficial slide)
Pinされたオブジェクトの mutable reference の取得は安全でない!
なぜなら std::mem::swap で move される危険があるため
Pinされたオブジェクトは、
unsafeコード以外からは mutable reference を取得できない
safe関数でラップして通常操作での安全性を担保する
immutable reference の取得は問題なく行える
immutable reference からは move をすることができないため
つまり Pin されたオブジェクトは以下のように振る舞えば良い