Upgrade to Pro — share decks privately, control downloads, hide ads and more …

簡単なシェルを作ってRustを学ぼう

TaKO8Ki
November 08, 2022

 簡単なシェルを作ってRustを学ぼう

TaKO8Ki

November 08, 2022
Tweet

More Decks by TaKO8Ki

Other Decks in Programming

Transcript

  1. © Money Forward, Inc.
    簡単なシェルを作って
    Rustを学ぼう🦀
    TaKO8Ki
    2022/10/27 Rustハンズオンイベント

    View Slide

  2. © Money Forward, Inc.
    ● A member of
    Rust compiler team
    contributors
    ● Love OSS
    ● Live in Kyoto
    I’m TaKO8Ki.
    GitHub: @TaKO8Ki

    View Slide

  3. © Money Forward, Inc.
    はじめに

    View Slide

  4. © Money Forward, Inc.
    はじめに
    2時間弱のイベントなので、
    自由に寝そべったり、好みの姿勢を取った
    り、ふらっとトイレに行ったりしていただ
    いて問題ないです!

    View Slide

  5. © Money Forward, Inc.
    はじめに
    CommentScreenを使ってコメントできます


    View Slide

  6. © Money Forward, Inc.
    はじめに
    https://commentscreen.com/comments?room=rust-hands-on
    QRコードはしばらくスライドの端に載せておきます
    room nameは「rust-hands-on」
    CommentScreenを使ってコメントできます

    View Slide

  7. © Money Forward, Inc.
    Table of Contents
    ● 今日作るもの
    ● Rustの基本的なところ
    ● ハンズオン 🦀
    rust-hands-on

    View Slide

  8. © Money Forward, Inc.
    今日作るもの

    View Slide

  9. © Money Forward, Inc.
    今日作るもの

    View Slide

  10. © Money Forward, Inc.
    Rustの基本的なとこ

    View Slide

  11. © Money Forward, Inc.
    Rustの基本的なところ
    まだRustを書いたことがない人は、
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    https://www.rust-lang.org/tools/install
    rust-hands-on

    View Slide

  12. © Money Forward, Inc.
    Rustの基本的なところ
    使いそうなツール

    # Build
    $ cargo build
    # Lint
    $ cargo clippy
    # Format
    $ cargo fmt
    rust-hands-on

    View Slide

  13. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    LSPを使うなら、
    RustのためのLSP実装。IDE用のRustコンパイラフロントエ
    ンド。
    VSCodeならExtensionを入れるだけ。
    https://rust-analyzer.github.io/manual.html#installation

    View Slide

  14. © Money Forward, Inc.
    Rustのどこが難しいと思いま
    すか?

    View Slide

  15. © Money Forward, Inc.
    所有権?

    View Slide

  16. © Money Forward, Inc.
    borrow checker?

    View Slide

  17. © Money Forward, Inc.
    lifetime?

    View Slide

  18. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    メモリマネジメントの手法
    ● 手動でメモリ管理する
    ● Garbage collection

    View Slide

  19. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    メモリマネジメント
    ● 手動でメモリ管理する
    ● Garbage collection
    ● RAII - Resource Acquisition Is Initialization
    > 日本語では「リソース取得は初期化である」「リソースの確保は初期化時に」
    「リソースの取得と初期化」などの意味を持ち、資源(リソース)の確保と解放
    を、クラス型の変数の初期化と破棄処理に結び付けるというプログラミングのテク
    ニックである。
    https://ja.wikipedia.org/wiki/RAII

    View Slide

  20. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    Rustのメモリマネジメント
    Rustは、所有権と借用によってエイリアシングとミューテーションの両方
    が同時に起こらないことを保証している。

    View Slide

  21. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    let s = String::new();
    所有権(ownership)とは?
    ● 新しいオブジェクトを作ると、代入された変数はそのオブジェクトの
    オーナーになる。
    ● いかなる時もオーナーは一つだけ。
    ● オーナーがスコープを抜けたらオブジェクトはドロップされる。

    View Slide

  22. © Money Forward, Inc.
    Rustの基本的なところ
    所有権(ownership)とは?
    fn main() {
    let a = String::new("foo");
    let b = s;
    println!("{}", a);
    }
    rust-hands-on

    View Slide

  23. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    所有権(ownership)とは?
    error[E0382]: borrow of moved value: `s`
    --> src/main.rs:4:20
    |
    2 | let s = String::new();
    | - move occurs because `s` has type `String`,
    which does not implement the `Copy` trait
    3 | let p = s;
    | - value moved here
    4 | println!("{}", s);
    | ^ value borrowed here after move
    |

    View Slide

  24. © Money Forward, Inc.
    Rustの基本的なところ
    所有権(ownership)とは?
    fn main() {
    let a = String::new("foo");
    let b = s; // bが新しいオーナー
    println!("{}", a);
    }
    rust-hands-on

    View Slide

  25. © Money Forward, Inc.
    Rustの基本的なところ
    所有権(ownership)とは?
    fn main() {
    let a = String::new("hello");
    let b = s; // bが新しいオーナー
    println!("{}", a); // moveされた値を使っているのでエラー
    }
    メモリの二重解放を防ぐことができる。
    rust-hands-on

    View Slide

  26. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    moveが起こらないケース
    fn main() {
    let a: u32 = 3;
    let b = a; // コピー
    println!("{}", a);
    }
    Copy Traitが実装されている型はmoveが起こらず値がコピーされる。

    View Slide

  27. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    所有権(ownership)とは?
    Copy Traitが実装されている型の一部:
    ● 整数型 (u32など)
    ● bool
    ● 浮動小数点型 (f64)
    ● char。
    ● Copyの型だけを含むタプル。
    例えば、(i32, i32) はこれに含まれるが、 (i32, String) は含まれない。

    View Slide

  28. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    借用(borrow)とは?
    fn main() {
    let a = String::from("hello");
    let b = &a;
    a.len();
    b.len();
    }

    View Slide

  29. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    借用(borrow)とは?
    fn main() {
    let a = String::from("hello");
    let b = &a;
    let c = &a; // 複数借用することも可能
    a.len();
    b.len();
    c.len();
    }

    View Slide

  30. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    借用(borrow)とは?
    fn main() {
    let a: &Vec;
    {
    let b = Vec::new();
    a = &b;
    } // bはここでドロップされる
    a.len(); // オーナーbがドロップされているのでエラー
    }
    エイリアスは可能だが参照はオブジェクトより長生きできない

    View Slide

  31. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    借用(borrow)とは?
    error[E0597]: `b` does not live long enough
    --> src/main.rs:5:13
    |
    5 | a = &b;
    | ^^ borrowed value does not live long
    enough
    6 | } //v is dropped here
    | - `b` dropped here while still borrowed
    7 | a.len(); //error:borrowed value does not live
    long enough
    | ------- borrow later used here

    View Slide

  32. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    ミュータブルな借用
    fn main() {
    let mut a = String::from("hello");
    let b = &mut a; // 1つ目のミュータブルな参照
    let c = &mut a; // 2つ目のミュータブルな参照
    b.push_str("a"); // cannot borrow `a` as mutable
    more than once at a time
    }
    ミュータブルな参照は一度に一つだけ。

    View Slide

  33. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    ミュータブルな借用
    fn main() {
    let mut a = vec![0, 1, 2, 3];
    let b = &a[0]; // vecの最初の要素へのイミュータブルな参照
    a.clear();
    let c = *b; // 無効なメモリにアクセスする可能性がある
    }
    イミュータブル、ミュータブルな参照が同時に可能だと、

    View Slide

  34. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    ライフタイム
    fn main() {
    let r; // ---------+-- 'a
    // |
    { // |
    let x = 5; // -+-- 'b |
    r = &x; // | |
    } // -+ |
    // |
    println!("r: {}", r); // |
    } // ---------+
    全ての参照はライフタイムを持っている。
    bがaより小さい

    View Slide

  35. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    ライフタイム
    fn main() {
    let x = 5; // ----------+-- 'b
    // |
    let r = &x; // --+-- 'a |
    // | |
    println!("r: {}", r); // | |
    // --+ |
    } // ----------+
    コンパイルを通すには、
    bがaより大きくなった

    View Slide

  36. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";
    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
    }
    長い方のStringを返すような関数を書いたとする。

    View Slide

  37. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
    x
    } else {
    y
    }
    }
    長い方のStringを返すような関数を書いたとする。

    View Slide

  38. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    下記のようにコンパイルエラーになる。
    error[E0106]: missing lifetime specifier
    --> src/main.rs:9:33
    |
    9 | fn longest(x: &str, y: &str) -> &str {
    | ---- ---- ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but the
    signature does not say whether it is borrowed from `x` or `y`
    help: consider introducing a named lifetime parameter
    |
    9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    | ++++ ++ ++ ++

    View Slide

  39. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
    x
    } else {
    y
    }
    }
    コンパイラはx、yどちらが返り
    値になるか分からないので返り
    値のライフタイムが有効かも分
    からない

    View Slide

  40. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    error[E0106]: missing lifetime specifier
    --> src/main.rs:9:33
    |
    9 | fn longest(x: &str, y: &str) -> &str {
    | ---- ---- ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but the
    signature does not say whether it is borrowed from `x` or `y`
    help: consider introducing a named lifetime parameter
    |
    9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    | ++++ ++ ++ ++

    View Slide

  41. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
    {
    if x.len() > y.len() {
    x
    } else {
    y
    }
    }
    引数と返り値にライフタイムをコンパイラに教えてやる。

    View Slide

  42. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    ライフタイム注釈
    &Vec // 参照
    &'a Vec // ライフタイムを明示した参照
    &'a mut Vec // ライフタイムを明示したミュータブルな参照
    この注釈は「少なくともライフタイム 'aと同じだけ生きる」ということを意味している。

    View Slide

  43. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    fn main() {
    let string1 = String::from("long string is long" );
    {
    let string2 = String::from("xyz");
    let result = longest(string1.as_str(),
    string2.as_str());
    println!("The longest string is {}", result);
    } // string2、resultはここまで生きる
    } // string1 はここまで生きる
    コンパイルできる例

    View Slide

  44. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    fn main() {
    let string1 = String::from("long string is long" );
    let result;
    {
    let string2 = String::from("xyz");
    result = longest(string1.as_str(), string2 .as_str());
    } // string2はここまで生きる
    println!("The longest string is {}", result);
    } // string1、resultはここまで生きる
    コンパイルできない例
    返り値のライフタイムは引数の内小さい方のもの(string2のライフタイム)と同じと解釈される

    View Slide

  45. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    関数に対するライフタイム
    コンパイルできない例
    error[E0597]: `string2` does not live long enough
    --> src/main.rs:6:44
    |
    6 | result = longest(string1.as_str(),
    string2.as_str());
    | ^^^^^^^^^^^^^^^^
    borrowed value does not live long enough
    7 | }
    | - `string2` dropped here while still borrowed
    8 | println!("The longest string is {}", result);
    | ------ borrow
    later used here

    View Slide

  46. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    Structへのライフタイム注釈
    struct ImportantExcerpt<'a> {
    part: &'a str,
    }

    View Slide

  47. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    Methodへのライフタイム注釈
    impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
    3
    }
    }

    View Slide

  48. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    Staticなライフタイム
    let s: &'static str = "I have a static lifetime."
    ;
    特別なライフタイムとして、'staticがある。
    これは、その参照がプログラムが終了するまで生き続けることを示している。
    static NUM: i32 = 18;
    'staticライフタイムを注釈して定数を定義する。

    View Slide

  49. © Money Forward, Inc.
    rust-hands-on
    Rustの基本的なところ
    staticとconst
    static FOO: &str = "foo";
    const BAR: i32 = 10;
    ● const: 不変な値
    ● static: 'staticライフタイムを持つ変更可能な値。mutableな物へのアクセスや
    変更はunsafeです。

    View Slide

  50. © Money Forward, Inc.
    ハンズオン 🦀

    View Slide

  51. © Money Forward, Inc.
    rust-hands-on
    ハンズオン 🦀
    雛形を作ろう
    PRに沿って実装していく用のRepository(途中まで):
    https://github.com/TaKO8Ki/smash
    完全版Repository: https://github.com/TaKO8Ki/toy-shell
    ※ 自分もシェル初心者なのでバグやら実装ミスやらあればissue投げてい
    ただけるとありがたいです。(toy-shellの方へ)

    View Slide

  52. © Money Forward, Inc.
    rust-hands-on
    ハンズオン 🦀
    雛形を作ろう
    $ cargo new # crate作る
    # first commit見てmain.rsとConfig.tomlをコピペ。CI回す場合
    は、.github/workflows/ci.ymlも
    $ cargo run # 実行
    $ RUST_LOG=smash::event=debug cargo run
    $ cargo test # #[test] cfg(test)

    View Slide

  53. © Money Forward, Inc.
    rust-hands-on
    ハンズオン 🦀
    多分ここまでが限界
    - Implement exit
    https://github.com/TaKO8Ki/smash/pull/7
    - Visit simple command
    https://github.com/TaKO8Ki/smash/pull/6

    View Slide

  54. © Money Forward, Inc.
    参考
    ● HashRust - experieance the joy of Rust https://hashrust.com
    ● Validating References with Lifetimes
    https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html
    ● constants - Rust By Example
    https://doc.rust-lang.org/rust-by-example/custom_types/constants.html
    ● A command-line shell like fish, but POSIX compatible.
    https://github.com/nuta/nsh

    View Slide

  55. View Slide