1 of 78

Pinを追うにはRustを追わねば�(体験版)

hsjoihs

2 of 78

Part 1.

?Sized 構文

3 of 78

皆さんは

4 of 78

皆さんは

str型の変数

5 of 78

皆さんは

str型の変数

って見たことありますか?

6 of 78

fn main() {

let x : str;

}

7 of 78

fn main() {

let x : str;

}

8 of 78

fn main() {

let x : str;

}

str型の値のサイズはコンパイル時に知ることができない

9 of 78

型が何バイトなのかを知るのは大事

10 of 78

コンパイル時に何バイトなのか分からないと…

11 of 78

コンパイル時に何バイトなのか分からないと…

  • スタックに置けない

12 of 78

コンパイル時に何バイトなのか分からないと…

  • スタックに置けない
  • ゆえに let x: str = …; とは書けない

13 of 78

コンパイル時に何バイトなのか分からないと…

  • スタックに置けない
  • ゆえに let x: str = …; とは書けない
  • あれ、じゃあ let x: T = …; って書けなかったりする?

14 of 78

コンパイル時に何バイトなのか分からないと…

  • スタックに置けない
  • ゆえに let x: str = …; とは書けない
  • あれ、じゃあ let x: T = …; って書けなかったりする?
  • 困る!

15 of 78

えっでも let x: T = …;いたことあるよ

16 of 78

えっでも let x: T = …;書いたことあるよ

はい、書けます。

fn identity<T>(a: T) -> T {

let b: T = a;

b

}

17 of 78

えっでも let x: T = …;書いたことあるよ

はい、書けます。

fn identity<T>(a: T) -> T {

let b: T = a;

b

}

何故!?

18 of 78

なぜかというと

19 of 78

なぜかというと

  • let x: T = …; と書きたい

20 of 78

なぜかというと

  • let x: T = …; と書きたい
  • じゃあ、書けるようにルールを決めよう

21 of 78

なぜかというと

  • let x: T = …; と書きたい
  • じゃあ、書けるようにルールを決めよう
  • fn foo<T>() 書いたら
    • T のサイズはコンパイル時に分かる (Sizedである) ものとする

22 of 78

23 of 78

24 of 78

25 of 78

  • fn foo<T>() と書いたら
    • T のサイズはコンパイル時に分かる (Sizedである) ものとする

26 of 78

  • fn foo<T>() と書いたら
    • T のサイズはコンパイル時に分かる (Sizedである) ものとする

  • Sizedであるという前提に疑問を投げかける際には
    • <T: ?Sized> と疑う

27 of 78

Part 2.

?Move 案

28 of 78

「ムーブされると困る値」

29 of 78

「ムーブされると困る値」

  • 自己参照構造体など

30 of 78

「ムーブされると困る値」

  • 自己参照構造体など

左図は https://www.slideshare.net/HiroakiGoto/stdpin の p.5 から引用

31 of 78

「ムーブされると困る値」

  • C++「コピーコンストラクタとムーブコンストラクタを潰せばOK」

32 of 78

「ムーブされると困る値」

  • C++「コピーコンストラクタとムーブコンストラクタを潰せばOK」
  • Rust「ムーブコンストラクタなんてものはありません、常にmemcpyです」

33 of 78

Rustムーブは常にmemcpyです

34 of 78

Rust「ムーブは常にmemcpyです」

  • C++のムーブコンストラクタは例外を投げることができる

35 of 78

Rust「ムーブは常にmemcpyです」

  • C++のムーブコンストラクタは例外を投げることができる
    • 「例外を投げてはいけない」だと達人しかムーブコンストラクタを書けないから

36 of 78

Rust「ムーブは常にmemcpyです」

  • C++のムーブコンストラクタは例外を投げることができる
    • 「例外を投げてはいけない」だと達人しかムーブコンストラクタを書けないから
    • 代わりに std::move_if_noexcept が入った

37 of 78

Rust「ムーブは常にmemcpyです」

  • C++のムーブコンストラクタは例外を投げることができる
    • 「例外を投げてはいけない」だと達人しかムーブコンストラクタを書けないから
    • 代わりに std::move_if_noexcept が入った
  • つまりC++は「代入」「引数を渡す」「関数から返る」とかでunwindの可能性

38 of 78

Rust「ムーブは常にmemcpyです」

  • C++のムーブコンストラクタは例外を投げることができる
    • 「例外を投げてはいけない」だと達人しかムーブコンストラクタを書けないから
    • 代わりに std::move_if_noexcept が入った
  • つまりC++は「代入」「引数を渡す」「関数から返る」とかでunwindの可能性
  • Rust にそれを採用したら、おちおちunsafeとか書けない。困る→「Rust では常にmemcpyです」
    • “Values of all types are moved via memcpy. This makes writing generic unsafe code much simpler since assignment, passing and returning are known to never have a side effect like unwinding.” https://prev.rust-lang.org/en-US/faq.html#does-rust-have-move-constructors

39 of 78

余談:自己参照構造体以外に「困る値」ないの?

40 of 78

余談:自己参照構造体以外に「困る値」ないの?

41 of 78

余談:自己参照構造体以外に「困る値」ないの?

42 of 78

「ムーブされると困る値」の対処

43 of 78

?Sizedと同じことをしよう!

44 of 78

?Sizedと同じことをしよう!

  • デフォルトでMove属性(ムーブしていいよ!)

45 of 78

?Sizedと同じことをしよう!

  • デフォルトでMove属性(ムーブしていいよ!)
  • 必要があればそれを疑う

46 of 78

問題点

47 of 78

問題点

  • 一切ムーブできない

48 of 78

問題点

  • 一切ムーブできない
    • コンストラクタからreturnもできない

49 of 78

問題点

  • 一切ムーブできない
    • コンストラクタからreturnもできない
    • 明示的に Foo {a: 1} とか書いて値を構築することになる

50 of 78

問題点

  • 一切ムーブできない
    • コンストラクタからreturnもできない
    • 明示的に Foo {a: 1} とか書いて値を構築することになる
    • privateフィールドどうするの?

51 of 78

問題点

  • 一切ムーブできない
    • コンストラクタからreturnもできない
    • 明示的に Foo {a: 1} とか書いて値を構築することになる
    • privateフィールドどうするの?
  • 困る

52 of 78

問題点

  • 一切ムーブできない
    • コンストラクタからreturnもできない
    • 明示的に Foo {a: 1} とか書いて値を構築することになる
    • privateフィールドどうするの?
  • 困る
  • 「借用されるまではムーブOK、とかは?」

53 of 78

問題点

  • 一切ムーブできない
    • コンストラクタからreturnもできない
    • 明示的に Foo {a: 1} とか書いて値を構築することになる
    • privateフィールドどうするの?
  • 困る
  • 「借用されるまではムーブOK、とかは?」

などなどいろいろあったのですが……

54 of 78

ポシャった

55 of 78

ポシャった

  • 互換性の破壊

56 of 78

ポシャった

  • 互換性の破壊

  • Pinが発明された

57 of 78

ポシャった

  • 互換性の破壊

  • Pinが発明された
    • コンパイラに変更を加えずに済む手段が発明された

58 of 78

Part 4.

やっとPin

59 of 78

最初に一番大事なことから

60 of 78

最初に一番大事なことから

  • 前述の理由で ?Move は言語に入らなかった

61 of 78

最初に一番大事なことから

  • 前述の理由で ?Move は言語に入らなかった
  • 「値はムーブできる」は疑えない前提である!

62 of 78

最初に一番大事なことから

  • 前述の理由で ?Move は言語に入らなかった
  • 「値はムーブできる」は疑えない前提である!
  • Pin?Move ではない!!

63 of 78

じゃあ Pin ってなんなのさ

64 of 78

じゃあ Pin ってなんなのさ

「ある値への参照 Pin<’a, P>ただし P は参照型であるとする)が誕生したら、

65 of 78

じゃあ Pin ってなんなのさ

「ある値への参照 Pin<’a, P> (ただし P は参照型であるとする)が誕生したら、

それ以降は参照されている値が動くようなことがあってはなりません

66 of 78

じゃあ Pin ってなんなのさ

「ある値への参照 Pin<’a, P> (ただし P は参照型であるとする)が誕生したら、

それ以降は参照されている値が動くようなことがあってはなりません

という契約※1 ※2

※1 ただし Unpin な値を指している Pin<’a, P> に関してはこの契約は無効

※2 dropreturn するか panic したときに契約は終了する

67 of 78

契約の破り方

68 of 78

契約の破り方

  • 値を普通にムーブする

69 of 78

契約の破り方

  • 値を普通にムーブする

  • 可変参照 &mut T を作る
    • std::mem::replacestd::mem::swap を呼ぶことで値を動かせるから

70 of 78

OKな例

// new() から返ってくる段階では、まだ Pin 参照が存在しない

// よって new() で値が返るときのムーブは契約に違反しない

let mut obj : Foo = Foo::new();

71 of 78

OKな例

// new() から返ってくる段階では、まだ Pin 参照が存在しない

// よって new() で値が返るときのムーブは契約に違反しない

let mut obj : Foo = Foo::new();

// obj への参照を Pin に教え、「こいつは二度と動きません」と誓約する

// unsafe、それは誓いの言葉

// さて、その結果できた Pin 参照に obj と命名する

// すると旧 obj をムーブする方法がなくなる

// 二度と旧 obj &mut を取ることもできない

let mut obj : Pin<&mut Foo> = unsafe { Pin::new_unchecked(&mut obj) };

72 of 78

OKな例

// 毎回誓約書に署名するのは面倒なので、

// pin_utils::pin_mut! マクロが便利

let obj = Foo::new();

pin_utils::pin_mut!(obj);

// new() から返ってくる段階では、まだ Pin 参照が存在しない

// よって new() で値が返るときのムーブは契約に違反しない

let mut obj : Foo = Foo::new();

// obj への参照を Pin に教え、「こいつは二度と動きません」と誓約する

// unsafe、それは誓いの言葉

// さて、その結果できた Pin 参照に obj と命名する

// すると旧 obj をムーブする方法がなくなる

// 二度と旧 obj &mut を取ることもできない

let mut obj : Pin<&mut Foo> = unsafe { Pin::new_unchecked(&mut obj) };

73 of 78

アウトな例

// new() から返ってくる段階では、まだ Pin 参照が存在しない

// よって new() で値が返るときのムーブは契約に違反しない

let mut obj = Foo::new();

// obj への参照を Pin に教え、「こいつは二度と動きません」と誓約する

// unsafe、それは誓いの言葉

// さて、その結果できた Pin 参照に obj と命名しない

// すると旧 obj をムーブする方法がある

// 再度、旧 obj &mut を取ることもできる

let mut obj2 = unsafe { Pin::new_unchecked(&mut obj) };

74 of 78

Final

結論

75 of 78

結論

76 of 78

結論

  • Pinは難しい

77 of 78

結論

  • Pinは難しい
  • 先人たちの卓越した発想に脱帽
    • 卓越してるので追うのがつらい……

78 of 78

以上です

ありがとうございました

hsjoihs