M.Hiroi's Home Page

Linux Programming

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

[ Home | Linux | Rust ]

Rust の基礎知識

●Box<T>

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

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

// Point を廃棄したときにメッセージを表示する
impl Drop for Point {
    fn drop(&mut self) {
        println!("Drop Point!");
    }
}

fn main() {
    let a = Box::new(10);
    println!("{}", *a);    // * でデリファレンス
                           // a でも表示できる
    let b = a;             // move
    // println!("{}", a);  コンパイルエラー
    println!("{}", b);
    {
        let p1 = Box::new(Point {x: 0.0, y: 0.0});
        println!("{}, {}", p1.x, p1.y);  // (*p1).x, (*p1).y と同じ
        let Point {x: c, y: d} = *p1;    // パターンマッチ
        println!("{}, {}", c, d);
    }
    // ここで p1 が廃棄される
    println!("----- end ------")
    // main() が終了したら b が廃棄される
}
10
10
0, 0
0, 0
Drop Point!
----- end ------
リスト : 簡単な連結リスト

#[derive(Debug)]
enum List {
    Nil,
    Cons(i32, Box<List>)
}

impl Drop for List {
    fn drop(&mut self) {
        println!("Drop List {:?}", self);
    }
}

impl List {
    // 空リストを返す
    fn new() -> List {
        List::Nil
    }

    // 連結リストの先頭にデータを追加する
    fn cons(self, x: i32) -> List {
        List::Cons(x, Box::new(self))
    }
}

fn main() {
    let a = List::new();
    let b = a.cons(1);       // a は move
    println!("{:?}", b);
    {
        let c = b.cons(2);   // b は move
        println!("{:?}", c);
    }
    // ここで c がすべて廃棄される
    println!("----- end -----");
}
Cons(1, Nil)
Cons(2, Cons(1, Nil))
Drop List Cons(2, Cons(1, Nil))
Drop List Cons(1, Nil)
Drop List Nil
----- end -----

●トレイトオブジェクト

リスト : トレイトオブジェクトの簡単な例題

trait Foo {
    fn method(&self);
}

struct Bar;
struct Baz;

impl Foo for Bar {
    fn method(&self) { println!("Bar method"); }
}

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

fn call_method(func: &dyn Foo) {
    func.method();    // 動的ディスパッチ
}

fn call_method_box(func: Box<dyn Foo>) {
    func.method();
}

fn main() {
    let x = Bar;
    let y = Baz;
    call_method(&x as &dyn Foo);
    call_method(&y);
    call_method_box(Box::new(x));
    call_method_box(Box::new(y));    
}
Bar method
Baz method
Bar method
Baz method
リスト : 図形オブジェクト

// 図形のトレイト
trait Figure {
    fn area(&self) -> f64;
    fn kind(&self) -> &str;

    // デフォルトメソッド
    fn print(&self) {
        println!("{}: area = {:.3}", self.kind(), self.area());
    }
}

// 三角形
struct Triangle {
    altitude: f64, base_line: f64
}

impl Triangle {
    fn new(a: f64, b: f64) -> Triangle {
        Triangle { altitude: a, base_line: b }
    }
}

impl Figure for Triangle {
    fn area(&self) -> f64 {
        self.altitude * self.base_line / 2.0
    }
    fn kind(&self) -> &str {
        "Triangle"
    }
}

// 四角形
struct Rectangle {
    width: f64, height: f64
}

impl Rectangle {
    fn new(w: f64, h: f64) -> Rectangle {
        Rectangle { width: w, height: h}
    }
}

impl Figure for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
    
    fn kind(&self) -> &str {
        "Rectangle"
    }
}

// 円
struct Circle {
  radius: f64
}

impl Circle {
    fn new(r: f64) -> Circle {
        Circle { radius: r }
    }
}

impl Figure for Circle {
    fn area(&self) -> f64 {
         std::f64::consts::PI * self.radius * self.radius
    }

    fn kind(&self) -> &str {
        "Circle"
    }
}

// 図形の合計値を求める
fn figure_sum(data: &[&dyn Figure]) -> f64 {
    let mut a = 0.0;
    for fig in data {
        a += fig.area();
    }
    a
}

fn main() {
    let a = Triangle::new(2.0, 2.0);
    let b = Rectangle::new(2.0, 2.0);
    let c = Circle::new(2.0);
    a.print();
    b.print();
    c.print();

    // トレイトオブジェクトを配列に格納する
    let d = [&a as &dyn Figure, &b as &dyn Figure, &c as &dyn Figure];
    println!("{:.3}", figure_sum(&d));
}
Triangle: area = 2.000
Rectangle: area = 4.000
Circle: area = 12.566
18.566

●クロージャの基本

リスト : クロージャの簡単な使用例

fn main() {
    let a = 10;
    let add_a = |x| x + a;      // 変数 a を immutable で借用
    println!("{}", add_a(20));
    let mut b = 0;
    {
        let mut inc_b = || b += 1;   // 変数 b を mutable で借用
        inc_b();
        inc_b();
        inc_b();
        inc_b();
        inc_b();
        // println!("{}", b);   コンパイルエラー
    }
    // mutable な借用は一つしか使用できない
    // inc_b が廃棄されると b にアクセスできる
    println!("{}", b);

    let mut c = 0;
    {
        // move クロージャ (所有権をクロージャに移動する)
        let mut add_c = move |x| {c += x; c};
        // 数値の場合、クロージャ内に値がコピーされ、
        // その値が書き換えられる
        println!("{}", add_c(100));
        println!("{}", add_c(200));
    }
    println!("{}", c);    // c の値は 0 のまま
}
30
5
100
300
0

●クロージャと高階関数

リスト : クロージャを受け取る高階関数

fn apply1<F, V>(func: F, x: V) -> V where F: Fn(V) -> V {
    func(x)
}

fn apply11<V>(func: &dyn Fn(V) -> V, x: V) -> V {
    func(x)
}

fn apply2<F, V>(func: F, x: V, y: V) -> V where F: Fn(V, V) -> V {
    func(x, y)
}

fn apply22<V>(func: &dyn Fn(V, V) -> V, x: V, y: V) -> V {
    func(x, y)
}

fn main() {
    fn square(x: f64) -> f64 { x * x }
    println!("{}", apply1(|x| x * x, 123));
    println!("{}", apply11(&|x| x * x, 123));
    println!("{}", apply1(square, 1.111));
    println!("{}", apply11(&square, 1.111));

    fn add(x: f64, y: f64) -> f64 { x + y }
    println!("{}", apply2(|x, y| x + y, 1, 2));
    println!("{}", apply22(&|x, y| x + y, 1, 2));
    println!("{}", apply2(add, 1.1, 2.2));
    println!("{}", apply22(&add, 1.1, 2.2));
}
15129
15129
1.234321
1.234321
3
3
3.3000000000000003
3.3000000000000003
リスト : 簡単な高階関数

// マッピング
fn map<F, T, U>(func: F, xs: &Vec<T>) -> Vec<U>
    where F: Fn(T) -> U, T: Copy, U: Copy {
    let mut ys: Vec<U> = vec![];
    for x in xs {
        ys.push(func(*x));
    }
    ys
}

// フィルター
fn filter<F, T>(pred: F, xs: &Vec<T>) -> Vec<T>
    where F: Fn(T) -> bool, T: Copy + PartialEq {
    let mut ys: Vec<T> = vec![];
    for x in xs {
        if pred(*x) { ys.push(*x); }
    }
    ys
}

// 畳み込み
fn reduce<F, T, U>(func: F, a: U, xs: &Vec<T>) -> U
    where F: Fn(U, T) -> U, T: Copy, U: Copy {
    let mut acc = a;
    for x in xs {
        acc = func(acc, *x);
    }
    acc
}

fn main() {
    let xs = vec![1,2,3,4,5,6,7,8];
    println!("{:?}", map(|x| x * x, &xs));
    println!("{:?}", map(|x| x as f64 * 1.1, &xs));
    println!("{:?}", filter(|x| x % 2 == 0, &xs));
    println!("{}", reduce(|a, x| a + x, 0, &xs));
}
[1, 4, 9, 16, 25, 36, 49, 64]
[1.1, 2.2, 3.3000000000000003, 4.4, 5.5, 6.6000000000000005, 7.700000000000001, 8.8]
[2, 4, 6, 8]
36

●クロージャを返す

リスト : クロージャを返す

// x を加算するクロージャを返す
fn make_adder(x: i32) -> Box<dyn Fn(i32) -> i32> {
    Box::new(move |y| x + y)
}

// 簡単なジェネレータ 
// (Rust にはイテレータがあるので、このような使い方はあまりしないと思う)
fn make_counter(init: i32) -> Box<dyn FnMut() -> i32> {
    let mut c = init - 1;
    Box::new(move || { c += 1; c})
}

fn main() {
    let add10 = make_adder(10);   // 10 を加算する関数になる
    let add20 = make_adder(20);   // 20 を加算する関数になる
    println!("{}", add10(1));
    println!("{}", add20(2));

    let mut cnt = make_counter(1);   // 1, 2, 3, ... を返す
    for _ in 0..10 {
        println!("{}", cnt());
    }
}
11
22
1
2
3
4
5
6
7
8
9
10

●ライフタイムの基本

リスト : ライフタイムパラメータの指定

// 配列の探索
fn find<'a, 'b, T: PartialEq>(item: &'b T, xs: &'a [T]) -> Option<&'a T> {
    for x in xs {
        if x == item { return Some(x); }
    }
    None
}

fn main() {
    let xs = [1,2,3,4,5,6,7,8];
    for x in 0 .. 10 {
        match find(&x, &xs) {
            Some(v) => println!("{}", v),
            None => println!("None")
        }
    }
}
None
1
2
3
4
5
6
7
8
None
リスト : メソッドのライフタイム

struct Foo<'a> {
    x: &'a i32
}

impl <'a> Foo<'a> {
    fn foo(&self) ->&i32 { self.x }

    fn foo1(&self, y: &i32) ->&i32 {
        println!("{},{}", self.x, y);
        self.x
    }    

    fn foo2<'b>(&self, y: &'b i32) ->&'b i32 {
        println!("{},{}", self.x, y);
        y
    }    
}

fn main() {
    let y = 123;
    let z = Foo { x: &y };
    println!("{}", z.foo());
    let y1 = 456;
    println!("{}", z.foo1(&y1));
    println!("{}", z.foo2(&y1));
}
123
123,456
123
123,456
456

●type

リスト : 点と距離

// f64 で二次元座標を保持する
struct Point1 {
    x: f64, y: f64
}

// f32 で二次元座標を保持する
struct Point2 {
    x: f32, y: f32
}

// f64 で三次元座標を保持する
struct Point3D {
    x: f64, y: f64, z: f64
}

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

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

impl Distance for Point2 {
    type Item = f32;
    fn distance(&self, p: &Self) -> Self::Item {
        let dx = self.x - p.x;
        let dy = self.y - p.y;
        (dx * dx + dy * dy).sqrt()
    }
}

impl Distance for Point3D {
    type Item = f64;
    fn distance(&self, p: &Self) -> Self::Item {
        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 = Point1 { x: 0.0, y: 0.0 };
    let p2 = Point1 { x: 10.0, y: 10.0 };
    let p3 = Point2 { x: 0.0f32, y: 0.0f32 };
    let p4 = Point2 { x: 10.0f32, y: 10.0f32 };
    let p5 = Point3D { x: 0.0, y: 0.0, z: 0.0 };
    let p6 = Point3D { x: 10.0, y: 10.0, z: 10.0 };
    println!("{}", p1.distance(&p2));
    println!("{}", p3.distance(&p4));
    println!("{}", p5.distance(&p6));
}
14.142135623730951
14.142136
17.320508075688775

●イテレータ

リスト : 整数列の生成

// start 以上 end 未満の整数列を生成する
struct IntSeq {
    start: i32, end: i32
}

// 初期化
impl IntSeq {
    fn new(s: i32, e: i32) -> IntSeq {
        IntSeq { start: s, end: e }
    }
}

// イテレータの実装
impl Iterator for IntSeq {
    type Item = i32;
    fn next(&mut self) -> Option<i32> {
        if self.start < self.end {
            let x = self.start;
            self.start += 1;
            Some(x)
        } else {
            None
        }
    }
}

fn main() {
    let mut s0 = IntSeq::new(0, 5);
    while let Some(x) = s0.next() {
        println!("{}", x);
    }
    for x in IntSeq::new(5, 10) {
        println!("{}", x);
    }
    let mut s1 = 10 .. 15;
    while let Some(x) = s1.next() {
        println!("{}", x);
    }
}
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
リスト : map, filter, fold, collect の簡単な使用例

fn change(x: i32) -> String {
    if x % 15 == 0 {
        "FizzBuzz".to_string()
    } else if x % 3 == 0 {
        "Fizz".to_string()
    } else if x % 5 == 0 {
        "Buzz".to_string()
    } else {
        format!("{}", x)
    }
}

fn main() {
    let xs: Vec<_> = (1..101).map(change).collect();
    println!("{:?}", xs);
    let ys = vec![1,2,3,4,5,6,7,8,9,10];
    {
        let a: Vec<_> = ys.iter().map(|x| x * x).collect();
        println!("{:?}", a);
        let b: Vec<_> = ys.iter().filter(|&x| x % 2 == 0).collect();
        println!("{:?}", b);
        println!("{}", ys.iter().fold(0, |a, x| a + x));
    }
    let c: Vec<_> = ys.into_iter().filter(|x| x % 2 == 1).collect();
    println!("{:?}", c);
    // println!("()", ys);   コンパイルエラー
}
["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"]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[2, 4, 6, 8, 10]
55
[1, 3, 5, 7, 9]

●モジュール

リスト : モジュールの使用例

mod foo {
    // 公開 (foo::bar::function() でアクセスできる)
    pub mod bar {
        pub fn function() {
            println!("call foo::bar::function");
        }
    }
    // 非公開 (foo の中でのみ有効)
    mod baz {
        pub fn function() {
            println!("call foo::baz::function");
        }

    }

    // 構造体 Foo (public)
    pub struct Foo {
        // フィールド a は public, b は private
        pub a: i32, b: i32
    }

    impl Foo {
        pub fn new(a: i32, b: i32) -> Foo {
            Foo { a: a, b: b }
        }
        pub fn add(&self) -> i32 { self.a + self.b }
    }

    pub fn function() {
        println!("call foo::function");
        baz::function();
    }
}

fn main() {
    foo::function();
    foo::bar::function();
    // foo::baz::function();   コンパイルエラー  

    let a = foo::Foo::new(1, 2);
    println!("{}", a.a);
    // println!("{}", a.b);    コンパイルエラー
    println!("{}", a.add());
}
call foo::function
call foo::baz::function
call foo::bar::function
1
3
test.rs        -- メインプログラム
foo/mod.rs     -- モジュール foo
   /bar/mod.rs -- モジュール bar
   /baz/mod.rs -- モジュール baz

    図 : ファイルの構成
リスト : メインプログラム

mod foo;

fn main() {
    foo::function();
    foo::bar::function();
    // foo::baz::function();   コンパイルエラー  

    let a = foo::Foo::new(1, 2);
    println!("{}", a.a);
    // println!("{}", a.b);    コンパイルエラー
    println!("{}", a.add());
}
リスト : モジュール foo/mod.rs

pub mod bar;
mod baz;

pub struct Foo {
    pub a: i32, b: i32
}

impl Foo {
    pub fn new(a: i32, b: i32) -> Foo {
        Foo { a: a, b: b }
    }
    pub fn add(&self) -> i32 { self.a + self.b }
}

pub fn function() {
    println!("call foo::function");
    baz::function();
}
リスト : モジュール foo/bar/mod.rs

pub fn function() {
    println!("call foo::bar::function");
}
リスト : モジュール foo/baz/mod.rs

pub fn function() {
    println!("call foo::baz::function");
}
$ rustc test.rs
$ ./test
call foo::function
call foo::baz::function
call foo::bar::function
1
3

●クレート

●cargo によるクレートの作成

●外部クレートの使い方

●エラー処理の基本

リスト : Option, Result と map() の使用例

fn main() {
    let a = [1, 2, 3, 4, 5];
    let r1 = a.iter().find(|&x| x % 3 == 0);
    println!("{}", r1.map(|&x| x * 10).unwrap());
    let r2 = a.iter().find(|&x| x % 6 == 0);
    println!("{}", r2.map(|&x| x * 10).unwrap_or(0));
    println!("{}", "12345".parse::<i32>().map(|x| x * 2).unwrap());
    println!("{}", "abcde".parse::<i32>().map(|x| x * 2).unwrap_or(0));
}
30
0
24690
0
リスト : 後置演算子 ? の使用例

fn parse_buff(buff: &[&str]) -> Result<Vec<i32>, std::num::ParseIntError> {
    let mut r: Vec<i32> = vec![];
    for s in buff {
        let n = s.parse()?;
        r.push(n)
    }
    Ok(r)
}

fn main() {
    let s0 = ["123", "456", "789"];
    println!("{:?}", parse_buff(&s0));
    let s1 = ["123", "abc", "789"];
    println!("{:?}", parse_buff(&s1));
}
Ok([123, 456, 789])
Err(ParseIntError { kind: InvalidDigit })

●ファイル入出力

リスト : 単純な echo (1)

use std::io::prelude::*;
use std::io::{self, BufReader, BufWriter};

// 標準入力 -> 標準出力 (バイト単位)
fn main() {
    let reader = BufReader::new(io::stdin());
    let mut writer = BufWriter::new(io::stdout());
    for c in reader.bytes() {
        writer.write(&[c.unwrap()]).unwrap();
    }
}
リスト : 単純な echo (2)

use std::io::prelude::*;
use std::io::{self, BufReader};

// 標準入力 -> 標準出力 (行単位)
fn main() {
    let reader = BufReader::new(io::stdin());
    for ls in reader.lines() {
        println!("{}", ls.unwrap());
    }
}
リスト : コマンドライン引数の取得 (test3.rs)

fn main() {
    for x in std::env::args() {
        println!("{}", x);
    }
}
$ ./test3 foo bar baz
./test3
foo
bar
baz
oops
リスト : ファイルの連結 (cat0.rs)

use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;

fn concat_file(filename: &String) -> std::io::Result<()> {
    let file = File::open(filename)?;
    let mut reader = BufReader::new(file);
    let mut buff = String::new();
    reader.read_to_string(&mut buff)?;
    println!("{}", buff);
    Ok(())
}

fn main() {
    for filename in std::env::args().skip(1) {
        match concat_file(&filename) {
            Ok(_) => (),
            Err(err) => println!("{}: {:?}", &filename, err)
        }
    }
}
リスト : ファイルのコピー (cp0.rs)

use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, BufWriter};

const BUFF_SIZE: usize = 128;

fn copy_file(src: &String, dst: &String) -> std::io::Result<()> {
    let infile = File::open(src)?;
    let outfile = File::create(dst)?;
    let mut buff: [u8; BUFF_SIZE] = [0; BUFF_SIZE];  // バッファ
    let mut reader = BufReader::new(infile);
    let mut writer = BufWriter::new(outfile);
    loop {
        let size = reader.read(&mut buff)?;
        writer.write(&mut buff[..size])?;
        if size < BUFF_SIZE { break; }
    }
    Ok(())
}

fn main() {
    let args: Vec<_> = std::env::args().collect();
    if args.len() < 3 {
        println!("usage: cp0 src_file dst_file");
    } else {
        match copy_file(&args[1], &args[2]) {
            Ok(_) => (),
            Err(err) => println!("{:?}", err)
        }
    }
}
リスト : ファイルのコピー (cp1.rs)

use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, BufWriter};

fn copy_file(src: &String, dst: &String) -> std::io::Result<()> {
    let infile = File::open(src)?;
    let outfile = File::create(dst)?;
    let mut reader = BufReader::new(infile);
    let mut writer = BufWriter::new(outfile);

    let mut buff: Vec<u8> = vec![];
    reader.read_to_end(&mut buff)?;
    // ベクタは immutable なスライスに変換できる
    writer.write_all(&buff)?;
    Ok(())
}

fn main() {
    let args: Vec<_> = std::env::args().collect();
    if args.len() < 3 {
        println!("usage: cp1 src_file dst_file");
    } else {
        match copy_file(&args[1], &args[2]) {
            Ok(_) => (),
            Err(err) => println!("{:?}", err)
        }
    }
}

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

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

[ Home | Linux | Rust ]