その式、値ですか?場所ですか?

Ba655e3712aaabfbca289fe136f85fe4?s=47 Masaki Hara
January 16, 2019

 その式、値ですか?場所ですか?

他の多くの手続き型言語と同様、Rustの式も右辺値(値)か左辺値(場所)のどちらかに分類されます。この値・場所にまつわる事実を整理してみました。この区別は決して難しいわけではないですが、整理して言語化することで、今後のRustプログラミングをより快適にすることができるかもしれません。

Ba655e3712aaabfbca289fe136f85fe4?s=128

Masaki Hara

January 16, 2019
Tweet

Transcript

  1. 1 2019/1/16 その式、値ですか?場所ですか? その式、値ですか? 場所ですか? 原 将己 @qnighy (ウォンテッドリー株式会社) Shinjuku.rs

    #2 @FORCIA 2019/01/16
  2. 2 2019/1/16 その式、値ですか?場所ですか? 本LTの目的 値と場所の区別を 意識することで、 より快適なRust生活を 送れるようにする

  3. 3 2019/1/16 その式、値ですか?場所ですか? 事前クイズ1 • 次の文は正しいでしょうか? 1 = 1;

  4. 4 2019/1/16 その式、値ですか?場所ですか? 事前クイズ2 • では、次の文は? *&mut 1 = 1;

  5. 5 2019/1/16 その式、値ですか?場所ですか? 式には値と場所がある • 「右辺値」「左辺値」ともいうお馴染みの概念 x = y; 場所

    (左辺値) 値 (左辺値)
  6. 6 2019/1/16 その式、値ですか?場所ですか? その式、値ですか、場所ですか? • では、次の場合はどうでしょう? let x = y.clone();

    値? 場所? というようなことを考えていくのが本LTの流れです
  7. 7 2019/1/16 その式、値ですか?場所ですか? Fact1: 式には値と場所がある

  8. 8 2019/1/16 その式、値ですか?場所ですか? 式には値と場所がある • 「右辺値」「左辺値」ともいうお馴染みの概念 x = y; 場所

    (左辺値) 値 (左辺値)
  9. 9 2019/1/16 その式、値ですか?場所ですか? デフォルトのモード • 以下の式はデフォルトで場所である x X ローカル変数、static変数 *e

    参照外し e.x フィールド e[x] インデックス
  10. 10 2019/1/16 その式、値ですか?場所ですか? デフォルトのモード • 以下の式は透過的に振る舞う (e) 括弧 e: T

    型帰属 (RFC2522, nightly)
  11. 11 2019/1/16 その式、値ですか?場所ですか? デフォルトのモード • 残りの式はデフォルトで値である f 定数、関数、コンストラクタ A 関数・メソッド呼出

    e.f() 各種リテラル・クロージャ f() A{} 42 "s" () (e,e) e..e [e;n] [e,e] 代入・複合代入 ||e x = e x += e アドレス演算子 &e 各種演算子 (&, *以外) -e e + e box e インラインアセンブリ e as T asm!() ブロック系構文 制御構文 {} if while for loop match unsafe try async return break continue yield e? このリストのうちいくつかはそもそも戻り値に深い意味 がない (! や () 型になる) let も同様だが、こいつは式ですらない
  12. 12 2019/1/16 その式、値ですか?場所ですか? デフォルトのモード • たとえば、 {} は () とは異なり、ムーブを強制する

    fn main() { let x = Box::new(42); println!("{}", {x}); println!("{}", {x}); // ERROR }
  13. 13 2019/1/16 その式、値ですか?場所ですか? • では、次のような場合は? let x = 42; &42

    + x Expect: 場所 Got: 値 Expect:値 Got: 場所 欲しいものが違うときは変換がはさまるはず
  14. 14 2019/1/16 その式、値ですか?場所ですか? Fact2: 暗黙変換を意識する

  15. 15 2019/1/16 その式、値ですか?場所ですか? 場所が欲しいときの変換 • 値なのに場所が欲しいときは、一時変数が生成される &42 let tmp =

    42; &tmp
  16. 16 2019/1/16 その式、値ですか?場所ですか? 場所が欲しいときの変換 • ただし、一定条件下では、変数ではなくstatic変数が生成され る (RFC1414, stable) &42

    static TMP = 42; &TMP
  17. 17 2019/1/16 その式、値ですか?場所ですか? 値が欲しいときの変換 • 場所なのに値が欲しいときは、読み取り動作 (コピーまたは ムーブ) が発生する x

    read(&x) 実際にはムーブ元のインバリデーションが必要なのでこのよう な単純化はできないが、コピーのときはこのように考えてし まって問題ない
  18. 18 2019/1/16 その式、値ですか?場所ですか? 事前クイズ2の答え • これはコンパイルが通る *&mut 1 = 1;

    1が入った一時変数が作られる そこに1を代入する
  19. 19 2019/1/16 その式、値ですか?場所ですか? 例外 • 代入演算子と複合代入演算子では、左辺を構文的に以下のいず れかに制限している: x X ローカル変数、static変数

    *e 参照外し e.x フィールド e[x] インデックス
  20. 20 2019/1/16 その式、値ですか?場所ですか? 事前クイズ1の答え • 前述の例外条件に引っかかるので、次の文はコンパイルが通ら ない 1 = 1;

  21. 21 2019/1/16 その式、値ですか?場所ですか? Fact3: パターンマッチにも 値と場所がある

  22. 22 2019/1/16 その式、値ですか?場所ですか? パターンマッチにも値と場所がある • マッチ対象の式のモード = パターンのモード if let

    Some(ref mut s) = s { s.push_str("world!"); } if let Some(s) = s { eprintln!("{}", s); } 場所に対するマッチング 値に対するマッチング let, match, if let, while let で共通して現れる現象。 引数と for 文の場合は必ず値が渡ってくる
  23. 23 2019/1/16 その式、値ですか?場所ですか? 例: Optionの場所マッチ • Optionのヘルパー関数はほとんどが値マッチで、場所マッチを するのは以下の関数のみ get_or_insert_with get_or_insert

    iter_mut iter as_mut as_ref is_some is_none take, replace はやや特殊で、 こいつらはそもそもマッチングをしていない 普遍的な場所マッチヘルパー
  24. 24 2019/1/16 その式、値ですか?場所ですか? 例: Optionの場所マッチ • 以下の例では Option の参照に対して処理をしているので、場 所マッチの王様である

    as_ref が必要、と理解できる let s: Option<String> = /* … */; s.as_ref().map(|s| s.as_str()).unwrap_or("bar");
  25. 25 2019/1/16 その式、値ですか?場所ですか? Fact4: 字句的にわからないケース

  26. 26 2019/1/16 その式、値ですか?場所ですか? 字句的にわからないケースもある • 代表的なのがこれ let x = y.clone();

    値? 場所?
  27. 27 2019/1/16 その式、値ですか?場所ですか? レシーバ • パターン1: autorefする場合 let shared =

    Arc::new(42); let shared2 = shared.clone(); Clone::clone(&shared)
  28. 28 2019/1/16 その式、値ですか?場所ですか? レシーバ • パターン2: autorefしない場合 fn foo(app: &Arc<App>)

    { if app.enabled { let app = app.clone(); // ... } } Clone::clone(app)
  29. 29 2019/1/16 その式、値ですか?場所ですか? マッチの既定モード (RFC2005) • 参照型の式を参照型でないパターンに入れた場合 fn foo(config: &Option<Config>)

    { if let Some(config) = config { // ... } } &Option<Config> Option<_>
  30. 30 2019/1/16 その式、値ですか?場所ですか? マッチの既定モード (RFC2005) • 以下のように補正されたものとして扱われる fn foo(config: &Option<Config>)

    { if let Some(ref config) = *config { // ... } } 式に参照外しがつく 全ての束縛にrefがつく
  31. 31 2019/1/16 その式、値ですか?場所ですか? • ボローやムーブの検査は型推論より後に行われるので、構文的 な構造が同じでも推論された型次第で挙動が変わることがある

  32. 32 2019/1/16 その式、値ですか?場所ですか? Fact5: 場所として脱糖される式

  33. 33 2019/1/16 その式、値ですか?場所ですか? 場所として脱糖される式 • 場所として脱糖されるやつがいる x X ローカル変数、static変数 *e

    参照外し e.x フィールド e[x] インデックス
  34. 34 2019/1/16 その式、値ですか?場所ですか? 場所として脱糖される式 • 場所として使われる式はunsizedでもいいので、次の文は合法: let s = "あいう";

    let ref t = s[3..6]; str 型 (参照束縛なので t は &str 型) str 型 (長さ不定)
  35. 35 2019/1/16 その式、値ですか?場所ですか? 場所として脱糖される式 • 次のように脱糖される let s = "あいう";

    let ref t = *Index::index(s, 3..6); * がつく
  36. 36 2019/1/16 その式、値ですか?場所ですか? Fact6: unsized

  37. 37 2019/1/16 その式、値ですか?場所ですか? 再掲 • 場所として使われる式はunsizedでもいいので、次の文は合法: let s = "あいう";

    let ref t = s[3..6]; str 型 (参照束縛なので t は &str 型) str 型 (長さ不定)
  38. 38 2019/1/16 その式、値ですか?場所ですか? Unsized Rvalues (RFC1909, nightly) • 値として使われる式でも、条件次第でunsizedで あることを許す

    • 主な動機は2つ: • Box<dyn FnOnce()> 等の値渡しオブジェクトの実 現 • [e; dyn n]: allocaに相当する処理
  39. 39 2019/1/16 その式、値ですか?場所ですか? Unsized Rvalues (RFC1909, nightly) • qnighyが実装しました! •

    #51131: 基本的なサポート • #54183: fn(self) をオブジェクト安全にする • まだやることは色々ある • 詳しくはまた別の機会に…… #![feature(unsized_locals)] fn foo(f: dyn FnOnce()) { f(); }
  40. 40 2019/1/16 その式、値ですか?場所ですか? まとめ • 値/場所にまつわる性質の整理を試みた。 • Fact1: 式には値と場所がある •

    Fact2: 暗黙変換は意識するに値する • Fact3: パターンマッチにも値と場所がある • Fact4: 字句的にわからないケースもある • Fact5: 場所として脱糖される式がある • Fact6: unsizedな値もある