M.Hiroi's Home Page

Linux Programming

お気楽 Rust プログラミング超入門

[ Home | Linux | Rust ]

Rust の基礎知識

●Hello, Rust!

●Hello, Cargo!

●基本的なデータ型

●変数

●基本的な演算子

リスト : 基本的なデータ型と演算子の使用例

fn main() {
    let a = 10;      // 通常の 10 進数は i32
    let b = 20;      // データ型の指定は 20i32 のように数字の後ろにつけてもよい
    let c = 1.234;   // 通常の小数点数は f64
    let d = 5.678;
    println!("{}", a + b);    // 文字列中の {} は引数の値を文字列に変換して出力 
    println!("{}", a - b);    // 書式指定は format! と Display を参照
    println!("{}", c * d);
    println!("{}", c / d);

    println!("{}", true && false);
    println!("{}", false || true);
    println!("{}", !true);

    let x = (100, 9.999, 'あ');    // タプル, 型は (u32, f64, char)
    println!("{}", x.0);
    println!("{}", x.1);
    println!("{}", x.2);
}
30
-10
7.006652
0.2173300457907714
false
true
false
100
9.999
あ

●基本的な制御構造

リスト : FizzBuzz 問題

fn main() {
    for n in 1..101 {
        if n % 15 == 0 {
            print!("FizzBuzz ");
        } else if n % 3 == 0 {
            print!("Fizz ");
        } else if n % 5 == 0 {
            print!("Buzz ");
        } else {
            print!("{} ", n);
        }
    }
}
$ ./fizzbuzz
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 
26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz 
Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz
 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz 
リスト : 数値積分 (円周率を求める)

fn main() {
    let n = 1000000;
    let w = 1.0 / n as f64;    // n を f64 にキャスト (Rust は as を使う)
    let mut s = 0.0;
    for i in 1 .. n + 1 {
        let x = (i as f64 - 0.5) * w;
        s += 4.0 / (1.0 + x * x);
    }
    println!("{}", s * w);
}
3.1415926535897643

●所有権

リスト : 所有権の移動

fn main() {
    let a = vec![1,2,3,4,5]; // vec! はベクタを生成するマクロ
    println!("{:?}", a);     // {:?} は Debug 表示
    let b = a;               // a から b に move
    println!("{:?}", b);
    // println!("{:?}", a);  コンパイルエラー
    let c: Vec<i32>;
    {
        let d = b;           // b から d に move
        // println!("{:?}", b);  コンパイルエラー
        println!("{:?}", d);
        c = d;               // d から c に move
                             // move しないと d が廃棄されるときにベクタも廃棄される
    }
    println!("{:?}", c);
}
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

●参照と借用

リスト : 参照と借用

fn main() {
    let a = 123;
    let ra = &a;
    let a1 = ra * 10;
    println!("{}", ra);
    println!("{}", a1);

    let mut b = 456;
    println!("{}", b);
    {
        let rb = &mut b;      // let mut rb とするとワーニング (mut は不要)
        // let rb1 = &b;      コンパイルエラー
        // println!("{}", b); コンパイルエラー
        println!("{}", rb);
        *rb = 1000;
    }
    println!("{}", b);
}
1230
123
456
456
1000

●スライス

リスト : 配列とスライスの簡単な使用例

fn main() {
    let a = [1,2,3,4,5,6,7,8];
    println!("{}", a[0]);
    println!("{}", a.len());
    let a1 = a;               // 基本的なデータ型は代入演算子でコピーされる
    println!("{:?}", a);
    println!("{:?}", a1);

    let mut b = [0; 5];
    b[0] = 10;
    println!("{}", b[0]);
    let mut b1 = b;           // mutable な配列もコピーされる
    b1[0] = 20;
    println!("{:?}", b);
    println!("{:?}", b1);
    
    let c = &a[2..5];         // immutable のスライス (参照) はいくつでも作れる
                              // 配列自体はコピーされない
    println!("{:?}", c);
    println!("{}", c.len());

    let d = &a[3..];
    println!("{:?}", d);
    println!("{}", d.len());

    {
        let e = &mut b[..];   // mutable な参照はひとつだけ
                              // let mut e はワーニング (mut は不要)
        e[1] = 20;
        println!("{}", e[1]);
        // b[1] = 30;   コンパイルエラー
    }
    b[1] = 30;                   // e が破棄されたので b にアクセスできる
    println!("{}", b[1]);
}
1
8
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8]
10
[10, 0, 0, 0, 0]
[20, 0, 0, 0, 0]
[3, 4, 5]
3
[4, 5, 6, 7, 8]
5
20
30

●文字列

リスト : 文字列の簡単な使用例

fn main() {
    let mut a = "hello, ".to_string();
    println!("{}", a);
    a.push_str("world");
    println!("{}", a);
    let b = a + ", oops!";
    println!("{}", b);
}
hello,
hello, world
hello, world, oops!

●関数

リスト : 関数の簡単な使用例

// 階乗 (再帰)
fn fact(n: i64) -> i64 {
    if n == 0 {
        1
    } else {
        n * fact(n - 1)
    }
}

// フィボナッチ数
fn fibo(n: i64) -> i64 {
    // 末尾再帰 (最適化は保証されていないようだ)
    fn fiboiter(n: i64, a: i64, b: i64) -> i64 {
        if n == 0 {
            a
        } else {
            fiboiter(n - 1, b, a + b)
        }
    }
    fiboiter(n, 0, 1)
}

// 配列の合計値を求める
fn sum_of(func: fn(i32) -> i32, seq: &[i32]) -> i32 {
    let mut acc: i32 = 0;
    for i in 0 .. seq.len() {
        acc += func(seq[i]);
    }
    acc
}

fn main() {
    for n in 10 .. 20 {
        println!("{}", fact(n));
    }
    for n in 40 .. 50 {
        println!("{}", fibo(n));
    }

    // 局所関数
    fn identity(x: i32) -> i32 { x }
    fn square(x: i32) -> i32 { x * x }
    fn cube(x: i32) -> i32 { x * x * x }

    let seq: [i32; 10] = [1,2,3,4,5,6,7,8,9,10];
    println!("{}", sum_of(identity, &seq));
    println!("{}", sum_of(square, &seq));
    println!("{}", sum_of(cube, &seq));
}
3628800
39916800
479001600
6227020800
87178291200
1307674368000
20922789888000
355687428096000
6402373705728000
121645100408832000
102334155
165580141
267914296
433494437
701408733
1134903170
1836311903
2971215073
4807526976
7778742049
55
385
3025

●パターンマッチ

let (a, b) = (1, 2);           // a = 1, b = 2
let (c, d) = ((3, 4), 5);      // c = (3, 4), d = 5
let (e, (f, g)) = (6, (7, 8)); // e = 6, f = 7, g = 8

        図 : タプルの分配束縛の例
リスト : &, ref , ref mut の使用例

fn main() {
    let ref x = 100;     // x の型は &i32, let x = &100; と同じ
    match x {
        &y => println!("{}", y)  // 参照と参照のマッチング (y の型は i32)
    }
    match *x {                   // デリファレンスしてもよい
        y => println!("{}", y)
    }
    let mut z = 200;
    match z {
        ref mut y => {           // y の型は &mut i32
            *y = 300;            // 値を書き換える
            println!("{}", y)
        }
    }
}
100
100
300
リスト : 簡単な連想リスト

// 探索
fn assoc(key: &str, data: &[(&str, i32)]) -> i32 {
    for &(x, v) in data {
        if x == key { return v; }
    }
    -1    
}

fn main() {
    let data = [("foo", 10), ("bar", 20), ("baz", 30)];
    println!("{}", assoc("foo", &data));
    println!("{}", assoc("bar", &data));
    println!("{}", assoc("baz", &data));
    println!("{}", assoc("oops", &data));
}
10
20
30
-1
リスト : 関数の仮引数でパターンマッチを使用する

fn foo(&(a, b): &(i32, i32)) {
    println!("{},{}", a, b);
}

fn main() {
    let x = (1, 2);
    foo(&x);
}
1,2

●構造体

リスト : 構造体の簡単な使用例

// 三次元の点
struct Point3D {
    x: f64, y: f64, z: f64
}

// 距離を求める
fn distance(p1: &Point3D, p2: &Point3D) -> f64 {
    let dx = p1.x - p2.x;    // 参照の場合でも 変数名.フィールド名 でアクセスできる
    let dy = p1.y - p2.y;    // (*変数名).フィールド名 と同じ
    let dz = p1.z - p2.z;
    (dx * dx + dy * dy + dz * dz).sqrt()
}

fn main() {
    let p1 = Point3D {x:0.0, y:0.0, z:0.0};
    let p2 = Point3D {x:10.0, y:10.0, z:10.0};
    println!("{}", distance(&p1, &p2));
}
17.320508075688775

●列挙型

リスト : 列挙型の簡単な使用例

// 果物を表すデータ型
enum Fruit {
    Apple, Banana, Grape, Orange
}

use Fruit::*;    // これで Fruit:: を省略できる

// 果物の価格
fn get_price(fruit: &Fruit) -> i32 {
    match *fruit {
        Apple => 200,
        Banana => 150,
        Grape => 300,
        Orange => 100
    }
}

fn main() {
    println!("{}", get_price(&Apple));
    println!("{}", get_price(&Banana));
    println!("{}", get_price(&Grape));
    println!("{}", get_price(&Orange));
}
200
150
300
100

●メソッド

リスト : メソッドの簡単な使用例

// 点
struct Point {
    x: f64, y: f64
}

// メソッドの定義
impl Point {
    fn new(x1: f64, y1: f64) -> Point {
        Point {x: x1, y: y1}
    }

    fn distance(&self, p: &Point) -> f64 {
        let dx = self.x - p.x;
        let dy = self.y - p.y;
        (dx * dx + dy * dy).sqrt()
    }
}

fn main() {
    let p1 = Point::new(0.0, 0.0);
    let p2 = Point::new(10.0, 10.0);
    let p3 = &p1;
    println!("{}", p1.distance(&p2));  // distance には p1 の参照が渡される
    println!("{}", p3.distance(&p2));  // 参照でも呼び出すことができる
}
14.142135623730951
14.142135623730951

●トレイトの基本

リスト : トレイトの簡単な使用例

// 距離を求める
trait Distance {
    fn distance(&self, p: &Self) -> f64;
}

// 二次元の点
struct Point {
    x: f64, y: f64
}

impl Point {
    fn new(x1: f64, y1: f64) -> Point {
        Point {x: x1, y: y1}
    }
}

// Distance の実装
impl Distance for Point {
    fn distance(&self, p: &Point) -> f64 {
        let dx = self.x - p.x;
        let dy = self.y - p.y;
        (dx * dx + dy * dy).sqrt()
    }
}

// 三次元の点
struct Point3D {
    x: f64, y: f64, z: f64
}

impl Point3D {
    fn new(x1: f64, y1: f64, z1: f64) -> Point3D {
        Point3D { x: x1, y: y1, z: z1 }
    }
}

// Distance の実装
impl Distance for Point3D {
    fn distance(&self, p: &Point3D) -> f64 {
        let dx = self.x - p.x;
        let dy = self.y - p.y;
        let dz = self.z - p.z;
        (dx * dx + dy * dy + dz * dz).sqrt()
    }
}

fn main() {
    let p1 = Point::new(0.0, 0.0);
    let p2 = Point::new(10.0, 10.0);
    let p3 = Point3D::new(0.0, 0.0, 0.0);
    let p4 = Point3D::new(10.0, 10.0, 10.0);
    println!("{}", p1.distance(&p2));
    println!("{}", p3.distance(&p4));    
}
14.142135623730951
17.320508075688775

●トレイトの継承

リスト : トレイトの継承

trait Foo {
    fn method_a(&self);
}

trait Bar : Foo {
    fn method_b(&self);
}

struct Baz;

impl Foo for Baz {
    fn method_a(&self) {
        println!("method_a!");
    }
}

impl Bar for Baz {
    fn method_b(&self) {
        println!("method_b!");
    }
}

fn main() {
    let a = Baz;
    a.method_a();
    a.method_b();
}
method_a!
method_b!

●Derive

リスト : Derive の簡単な使用例

// 距離を求める
trait Distance {
    fn distance(&self, p: &Self) -> f64;
}

#[derive(Debug)]
struct Point {
    x: f64, y: f64
}

impl Point {
    fn new(x1: f64, y1: f64) -> Point {
        Point {x: x1, y: y1}
    }
}

// Distance の実装
impl Distance for Point {
    fn distance(&self, p: &Point) -> f64 {
        let dx = self.x - p.x;
        let dy = self.y - p.y;
        (dx * dx + dy * dy).sqrt()
    }
}

#[derive(Debug, PartialEq)]
struct Point3D {
    x: f64, y: f64, z: f64
}

impl Point3D {
    fn new(x1: f64, y1: f64, z1: f64) -> Point3D {
        Point3D { x: x1, y: y1, z: z1 }
    }
}

// Distance の実装
impl Distance for Point3D {
    fn distance(&self, p: &Point3D) -> f64 {
        let dx = self.x - p.x;
        let dy = self.y - p.y;
        let dz = self.z - p.z;
        (dx * dx + dy * dy + dz * dz).sqrt()
    }
}

fn main() {
    let p1 = Point::new(0.0, 0.0);
    let p2 = Point::new(10.0, 10.0);
    let p3 = Point3D::new(0.0, 0.0, 0.0);
    let p4 = Point3D::new(10.0, 10.0, 10.0);
    println!("{:?}", p1);
    println!("{:?}", p2);
    println!("{:?}", p3);
    println!("{:?}", p4);
    println!("{}", p3 == p4);
    println!("{}", p3 != p4);
    println!("{}", p4 == p4);
    println!("{}", p4 != p4);
    println!("{}", p1.distance(&p2));
    println!("{}", p3.distance(&p4));
}
Point { x: 0.0, y: 0.0 }
Point { x: 10.0, y: 10.0 }
Point3D { x: 0.0, y: 0.0, z: 0.0 }
Point3D { x: 10.0, y: 10.0, z: 10.0 }
false
true
true
false
14.142135623730951
17.320508075688775

●ジェネリクスの基本

リスト : ジェネリクスの簡単な使用例

// 配列からデータを探す
fn find_if<T>(pred: fn(&T) -> bool, table: &[T]) -> Option<&T> {
    for x in table {
        if pred(x) { return Some(x); }
    }
    None
}

fn main() {
    fn evenp(x: &i32) -> bool { x % 2 == 0 }
    fn oddp(x: &i32) -> bool { x % 2 != 0 }
    let a = [2, 4, 6, 8, 10];
    match find_if(evenp, &a) {
        Some(v) => println!("{}", v),
        None => println!("None")
    }
    match find_if(oddp, &a) {
        Some(v) => println!("{}", v),
        None => println!("None")
    }
}
2
None
リスト : ジェネリクスの簡単な使用例 (2)

// 組
#[derive(Debug, PartialEq)]
struct Pair<T, U> {
    fst: T, snd: U
}

impl <T, U> Pair<T, U> {
    fn new(a: T, b: U) -> Pair<T, U> {
        Pair { fst: a, snd: b }
    }
}

fn main() {
    let p1 = Pair::new(1, 2.0);
    let p2 = Pair::new(1, 3.0);
    let p3 = Pair::new("foo", 100);
    println!("{:?}", p1);
    println!("{:?}", p2);
    println!("{:?}", p3);
    println!("{}", p1 == p2);
    println!("{}", p3 == p3);
}
Pair { fst: 1, snd: 2 }
Pair { fst: 1, snd: 3 }
Pair { fst: "foo", snd: 100 }
false
true

●型パラメータの境界

リスト : 境界条件の設定

//
// Distance, Point, Point3D の定義は省略
//

// 2点間の距離を表示する
// T は Distance を実装しているデータ型に限定される
fn print_distance<T: Distance>(p1: &T, p2: &T) {
    println!("{:.8}", p1.distance(p2));
}

fn main() {
    let p1 = Point::new(0.0, 0.0);
    let p2 = Point::new(10.0, 10.0);
    let p3 = Point3D::new(0.0, 0.0, 0.0);
    let p4 = Point3D::new(10.0, 10.0, 10.0);
    print_distance(&p1, &p2);
    print_distance(&p3, &p4);
}
14.14213562
17.32050808
リスト : 配列と連想リストの探索

// 配列の探索
fn find<T: PartialEq + Copy>(key: T, data: &[T]) -> bool {
    for &x in data {
        if key == x { return true; }
    }
    false
}

// 連想リストの探索
fn assoc<T: PartialEq + Copy, U>(key: T, data: &[(T, U)]) -> Option<&U> {
    for &(x, ref v) in data {
        if key == x { return Some(v); }
    }
    None
}

fn main() {
    let data = [1,2,3,4,5,6,7,8];
    println!("{}", find(8, &data));
    println!("{}", find(0, &data));

    let data1 = ["foo", "bar", "baz"];
    println!("{}", find("baz", &data1));
    println!("{}", find("oops", &data1));

    let data2 = [("foo", 100), ("bar", 200), ("baz", 300)];
    match assoc("baz", &data2) {
        Some(v) => println!("{}", v),
        None => println!("None")
    }
    match assoc("oops", &data2) {
        Some(v) => println!("{}", v),
        None => println!("None")
    }
}
true
false
true
false
300
None

初版 2017 年 7 月
改訂 2022 年 7 月 30 日

Copyright (C) 2017-2022 Makoto Hiroi
All rights reserved.

[ Home | Linux | Rust ]