読者です 読者をやめる 読者になる 読者になる

ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Rust 入門してる #1

Rust

最近 Rust を学び始めました。 まだちまちま構文を追っているところですが、ドキュメントがしっかりしていて学びやすい印象です。 (手元では、現在の最新の安定板である Rust 1.7 を使っています。)

Web 上で実行できる環境も提供されています。

進捗 (4.1 節から 4.6 節まで)

とりあえず変数結合 (variable bindings) から初めて、関数やプリミティブ型、コメント、if やループといった制御構文あたりをざっと見てみました。

まだここら辺はそんなに特徴的な部分はないですね。 char 型の保持する値が Unicode スカラ値である、とか、例外により処理が戻らない関数 (diverging functions) のための戻り値の型は ! で宣言、といったあたりが個人的にはちょっと新鮮でした。

次の節は 「Ownership」 だったり、その次は 「References and Borrowing」 だったりするので、そのあたりから Rust っぽさが出てきそうです。

サンプルコード

各節の内容をコードに落とし込んだものです。

fn main() {
    hello_variable_bindings();
    hello_functions();
    hello_primitive_types();
    hello_comments();
    hello_if();
    hello_loops();
}

// 4.1 Variable bindings
// See : https://doc.rust-lang.org/book/variable-bindings.html
fn hello_variable_bindings() {
    println!("=== 4.1 Variable bindings ===");

    // `let` で変数結合 (variable binding) の割り当てを行う。
    let a = 100;
    println!("`a` is {}", a);

    // `let` 式の左辺にはパターンも使える。
    let (b, c) = (1, 2);
    println!("`b` is {}, c is {}", b, c);

    // コロンの後に型を書いて型注釈 (type annotation) も可能。
    let d: i32 = 5;
    println!("`d` is {}", d);

    // 通常は結合は不変 (immutable) だが、`mut` で可変 (mutable) にできる。
    let mut e = 100;
    println!("`e` is {}", e);
    e = 200;
    println!("`e` is {}", e);

    // 結合の初期化は使用までに行われればよい。 (使用までに行われなければならない。)
    let f;
    f = 10;
    println!("`f` is {}", f);

    // スコープとシャドーイング。
    { // スコープはブロック内。
        let g = 20;
        println!("`g` is {}", g);
        // ブロックの外の結合を使用できる。
        println!("outer `f` is {}", f);
        // 同じ名前の結合で前の結合を覆い隠せる (shadow)。 (外のブロックの変数結合を隠す例。)
        let f = 30;
        println!("inner `f` is {}", f);
    }
    // ここからはもう上のブロック内の `z` は見えない。
    // 同じスコープの結合も隠せる (shadow)。
    let f = "Another type"; // Mutable な変数結合に割り当てし直す場合と異なり、別の型の値を割り当てられる。
    println!("`f` is {}", f);
}

// 4.2 Functions
// See : https://doc.rust-lang.org/book/functions.html
fn hello_functions() {
    println!("=== 4.2 Functions ===");

    // `fn` の後に関数名、括弧で囲まれた引数、ブレースを続けて関数定義。
    // 引数には型の宣言が必要。
    fn print_sum(x: i32, y: i32) {
        println!("sum is {}", x + y);
    }
    print_sum(1, 2);

    // 引数の戻り値の型は `->` に続けて宣言する。
    // 最後の式が戻り値になる。
    // (返り値の型が `unit` 型 (`()`) の場合は省略できるっぽい?)
    fn sum(x: i32, y: i32) -> i32 {
        x + y
    }
    print_sum(sum(1, 2), 3);

    // 最後にセミコロンがあると戻り値は `unit` 型になる。
    fn sum2(x: i32, y: i32) -> () {
        x + y;
    }
    sum2(1, 2);

    // Rust は主には式 (expression) ベースで、2 種類の文 (statement) を除いて他は全て式。
    // 1 つめの文、‘declaration statements’ について。
    // 例えば `let` による変数結合は文。 よって `let x = (let y = 100)` みたいな記述はできない。
    // 結合済みの変数への割り当ては式だが、その値は空のタプルになる。
    let mut v1 = 100;
    println!("v1 is {}", v1);
    let v2 = v1 = 200; // `v2` は `200` ではなく `()`。
    println!("v1 is {}, v2 is {:?}", v1, v2);

    // 2 つめの文、‘expression statements’ は、任意の式を文にするもの。
    // Rust では式と式をセミコロンで分割し、文の連なりとして記述していく。
    // (なので、上で見たように関数の最後にセミコロンがあるかどうかで返り値が変化する。)

    // `return` キーワード。
    fn foo(x: i32) -> i32 {
        if x < 0 { return x }
        x + 1
    }
    println!("`foo(-5)` is {}", foo(-5));
    println!("`foo(5)` is {}", foo(5));
    
    // 処理が戻らない diverging functions のための戻り値は `!` で宣言。
    fn do_panic() -> ! {
        // `panic!` マクロは現在のスレッドの実行をクラッシュさせる。
        panic!("Always panic");
    }
    if false { // 実際にはクラッシュしないように。
        // Diverging function の返り値は任意の型として扱える。
        let panic_return: i32 = do_panic();
    }
    
    // 関数を指す変数結合も生成できる。
    let sum_func: fn (i32, i32) -> i32 = sum;
    println!("`sum_func(5, 6)` is {}", sum_func(5, 6));
}

// 4.3 Primitive Types
// See : https://doc.rust-lang.org/book/primitive-types.html
fn hello_primitive_types() {
    println!("=== 4.3 Primitive Types ===");

    // `bool` 型
    let b1 = true;
    let b2: bool = false;
    println!("`b1` is {}, `b2` is {}", b1, b2);

    // `char` 型
    // 単一のユニコードスカラ値 (コードポイントから上位と下位のサロゲートコードポイントを除いたもの) を表す。 4 バイトらしい。
    let c1 = 'a';
    let c2: char = '💛';
    println!("`c1` is {}, `c2` is {}", c1, c2);
    
    // 各種数値型
    // 符号の有無 (signed/unsigned)、サイズ (fixed/variable)、
    // 整数か浮動小数点数 (integer/floating-point) でカテゴリ分けされる。
    // 整数は符号付きと符号無しがある。 
    // 符号付整数の型には `i` が、符号無整数には `u` が使用される。
    // Fixed size types は型にそれぞれのビット数 (8、16、32、64) が使用される。
    let num1: i32 = -100; // 32 ビット符号付整数
    let num2: u8 = 3; // 8 ビット符号無整数
    println!("`num1` is {}, `num2` is {}", num1, num2);
    // Variable sized types は、マシンのポインタサイズに依存するサイズ。
    // 符号付は `isize`、符号無は `usize`。
    let num3: usize = 8;
    println!("`num3` is {}", num3);
    // 浮動小数点数は `f32` と `f64`。
    // IEEE-754 single and double precision numbers に相当。
    let num4: f32 = 10.5;
    println!("`num4` is {}", num4);

    // 配列
    // 同一型の要素からなりサイズ固定。 `[T; N]` で型が表現される。
    // `T` は要素の型。 `N` はサイズ。
    let array1: [i32; 7] = [0, 1, 2, 3, 4, 5, 6];
    println!("`array1` is {:?}", array1);
    // `[elem; size]` という省略表記で、全ての要素を `elem` で初期化したサイズ `size` の配列を生成。
    let array2: [i32; 5] = [10; 5];
    println!("`array2` is {:?}", array2);
    // サイズは `len()` で取得可能、各要素は `[n]` で取得可能 (インデックスは 0 から開始)。
    println!("Size of `array1` is {}, `array1[0]` is {}", array1.len(), array1[0]);

    // Slice
    // メモリ上で隣接する指定の型の値の並びに対するビュー。
    // 以下は配列に対する例だが、配列以外の様々なものに対して `&` と `[]` の組み合わせで slice を生成できる。
    let complete_slice = &array1[..]; // `array1` のすべての要素を含む slice 
    let middle_slice = &array1[1..4]; // `array1` のインデックス 1 から 3 までの要素を含む slice
    println!("`complete_slice` is {:?}, `middle_slice` is {:?}", complete_slice, middle_slice);

    // `str`
    // これ自身は便利ではないが、`&str` という感じでリファレンスで使用すると便利である。
    let str1: &'static str = "Hello, world!";
    println!("`str1` is {}", str1);

    // タプル
    // 固定サイズの順序付きリスト。
    let tuple1: (i32, &str) = (1, "hello");
    println!("`tuple1` is {:?}", tuple1);
    // destructuring let によりタプル内のフィールドにアクセスできる。
    let (tuple1_elem1, tuple1_elem2) = tuple1;
    println!("`tuple1_elem1` is {}, `tuple1_elem2` is {}", tuple1_elem1, tuple1_elem2);
    // Indexing syntax により、各要素にアクセスすることもできる。
    println!("`tuple1.0` is {}, `tuple1.1` is {}", tuple1.0, tuple1.1);
    // 1 要素のタプルを括弧で囲まれた式と区別するために、カンマを使う。
    let tuple2 = (2,);
    let not_tuple = (2);
    println!("`tuple2` is {:?}, `not_tuple` is {}", tuple2, not_tuple);

    // 上で見たように関数も型を持つ。
    fn function1(x: i32) -> i32 { x }
    let function2: fn(i32) -> i32 = function1;
    println!("`function2` is {:?}", function2);
}

// 4.4 Comments
// See : https://doc.rust-lang.org/book/comments.html
fn hello_comments() {
    println!("=== 4.4 Comments ===");

    // 行コメントと doc コメントがある。
    // `//` で始まるコメントは行コメント。
    
    // doc コメントは `///` で始める。

    /// doc コメントのサンプル用の関数。
    ///
    /// # 例
    /// 以下のように使用する。
    /// ```
    /// do_test(); // 呼び出し
    /// ```
    fn do_test() {
        println!("hello");
    }
    do_test();

    // `///` による doc コメントは、そのコメントの後ろにあるものに対するコメント。
    // 別の形式の doc コメントとして、`//!` というものがある。
    // これは、そのコメントを含んでいるものに対するコメント。
    // モジュールや crate (crate とは?) の内部で使用されるらしい。

    // doc コメントについて、さらに詳しいことは次のページに書かれている。
    // https://doc.rust-lang.org/book/documentation.html
}

// 4.5 if
// See : https://doc.rust-lang.org/book/if.html
fn hello_if() {
    println!("=== 4.5 if ===");

    // 条件が括弧で囲まれないのと本体がブロック必須なこと以外はわりと C に近いっぽい。
    let x = 5;
    if x == 5 {
        println!("`x` is five!");
    } else if x == 6 {
        println!("`x` is six!");
    } else {
        println!("`x` is not five or six :(");
    }

    // 式なので値を返す。
    // (`else` がない場合は常に `()` になるらしい。)
    let y = if true { 10 } else { 20 };
    println!("`y` is {}", y);
}

// 4.6 Loops
// See : https://doc.rust-lang.org/book/loops.html
fn hello_loops() {
    println!("=== 4.6 Loops ===");

    // 現在のところ `loop`、`while`、`for` の 3 種類。
 
    // 明示的に抜けない限り無限にループする。
    loop {
        println!("In `loop`");
        break;
    }

    // 条件付き。
    let mut count = 0;
    while count < 4 {
        println!("In `while, with `count` {}", count);
        count += 1;
    }

    // `for`
    for x in 0..4 {
        println!("In `for`, with `x` {}", x);
    }

    // `.enumerate()` で index を取得できる。
    for (index, value) in (5..8).enumerate() {
        println!("`index` is {}, `value` is {}", index, value);
    }

    // ループの制御には `break` と `continue` が使用できる。
    let mut y = 0;
    loop {
        y += 1;
        if y % 2 == 0 { continue }
        if y > 4 { break }
        println!("`y` is {}", y);
    }

    // ループにラベルを付けて、`continue` や `break` で指定できる。
    'outer: for x in 0..5 {
        'inner: for y in 0..5 {
            if x % 2 == 0 { continue 'outer }
            if y % 2 == 0 { continue 'inner }
            println!("x: {}, y: {}", x, y);
        }
    }
}