2021-05-20 18:07
2021-05-20 18:07

Introduction - Rust By Example 日本語版 を見ながら Rust の基本文法をおさらいする

変数

  • 変数はデフォルトでイミュータブル
  • ミュータブルにしたい場合は mut キーワードを用いる
  • 宣言と定義を別々に行うことも可能
let x = 1;
// x = 2;  => Error

let mut y = 1;
let y = 2;

let z;
z = 123;
  • 例えば標準入力からデータを受け取る io::stdin.read_line は引数に mut &String をとる
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Error");
println!("Input value: {}", input);

関数とテスト

  • 関数は fn キーワードで定義できる
    • 明示的な return は必要なく、最後に評価された式が返却される
  • テストは同一のファイルに記述できる
pub fn add(a: u64, b: u64) -> u64 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        let value = add(1, 2);
        assert_eq!(value, 3);
    }
}

配列とタプル

  • コンパイル時にサイズが決定される配列がある
  • Tuple が標準の言語機能として存在する
let xs: [i32; 2] = [1, 2];
println!("{}", xs.len()); //=> 2

let a = (1, 2);
let b: i32 = a.0 + a.1;
let c: i32 = b.pow(2);

構造体・列挙体・定数

  • カスタム型は構造体 struct と 列挙型 enum として定義できる
  • 定数は const あるいは static というキーワードで定義できる
// Unit
struct Nil;
let _nil = Nil;

// Named Tuple
struct Pair(i32, i32);
let pair = Pair(1, 2);

// Classical Struct
struct Point {
    x: f32,
    y: f32,
}
let p = Point { x: 0.1, y: 0.2 };

// Enum
enum Event<T> {
    Next(T),
    Error,
    Complete,
}
impl<T> Event<T> {
    fn echo(&self) {
        match self {
            // Self は Event の type alias
            Self::Next(_) => println!("Next"),
            Self::Error => println!("Error"),
            Self::Complete => println!("Complete"),
        }
    }
}
  • struct や enum などは実装(メソッド)を持てる
  • メソッドのインターフェースを定める trait という言語機能もある
struct Adder {
    a: u32,
    b: u32,
}

impl Adder {
    fn new(a: u32, b: u32) -> Self {
        Adder { a, b }
    }

    fn calc(&self) -> u32 {
        (*self).a + (*self).b
    }
}

fn main() {
    let x = Adder::new(1, 3);
    println!("1 + 3 = {:?}", x.calc());  //=> 4
}
  • インスタンスメソッドの引数 self には次の 3 つの種類がある
    • &self: インスタンスメソッド
    • &mut self: 該当のオブジェクトがミュータブルである必要のあるインスタンスメソッド
    • self: オブジェクトのリソースを消費するインスタンスメソッド
struct Counter {
    value: u32,
}

impl Counter {
    fn new() -> Self {
        Counter { value: 0 }
    }

    fn add(&mut self) {
        self.value += 1;
    }

    fn peek(&self) -> u32 {
        self.value
    }

    fn destory(self) {
        // do nothing
    }
}

fn main() {
    let immutable_counter = Counter::new();
    println!("count: {:?}", immutable_counter.peek());
    // immutable_counter.add(); => ERROR
    // ミューテーションを起こすメソッドをコールするためには変数を let mut で宣言する必要がある
    // ただしオブジェクトの所有権を受け渡すような操作は受け付ける
    immutable_counter.destory();
    // println!("count: {:?}", counter.peek()); => moved out したあとのオブジェクトは操作できない

    let mut counter = Counter::new();
    println!("count: {:?}", counter.peek());
    counter.add();
    println!("count: {:?}", counter.peek());
    counter.add();
    println!("count: {:?}", counter.peek());
    counter.add();
    println!("count: {:?}", counter.peek());
    counter.destory(); //=> `counter` moved due to this method call
    println!("count: {:?}", counter.peek()); //=> Error: this function takes ownership of the receiver `self`, which moves `counter`
}

Box

  • Rust はすべての値をデフォルトでスタックに割り当てる
  • Box<T> を作成することにより値をヒープに割り当てられる
    • これはヒープ上に置いている T の値への単なるスマートポインタ
    • スコープを抜けるとデストラクタがコールされてヒープメモリが解放される
    • オペレータ *によりデリファレンスできる
fn main() {
    let boxed_number: Box<u128> = Box::new(1);
    let number: u128 = *boxed_number;
    println!("size: {:?}", mem::size_of_val(&boxed_number)); //=> size: 8(ポインタのサイズ)
    println!("size: {:?}", mem::size_of_val(&number)); //=> size: 16(128 bit = 8 bit * 16)
}

スコープとシャドーイング

  • 変数のシャドーイングも可能
fn main() {

    let x = 123;
    {
        let x = 234;
        println!("{:?}", x); //=> 234
    }
    println!("{:?}", x); //=> 123
}

リテラル

  • 数値の末尾に i, u, f などを置いて型を指定できる
let a = 12i8;
let b = 1234i16;
let c = 1234i32;
let d = 1234i64;
let e = 1234u128;
let f = 1234f32;

クロージャ

let f = |x| x + 1;
let res = f(1);
println!("{}", res); //=> 2

つづきはここから(作業メモ) https://doc.rust-jp.rs/rust-by-example-ja/conversion.html

Copyright © 53ningen.com