2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

1. はじめに

どうも、画像処理の研究を行っている大学院生です。
今度Rustを使用したアプリケーションを作ることになったのでUdemyの基礎から学ぶRustプログラミング入門を見て学んだことを残しておきます。

2. Rustの学習の流れ

2.1 環境構築
2.2 Rustの基本
2.3 フロー制御
2.4 所有権と参照
2.5 構造体と列挙型
2.6 クレートとモジュール
2.7 トレイトとジェネリクス
2.8 エラー処理
2.9 クロージャーとイテレータ
2.10 コレクション

2.1 環境構築

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/ .cargo/env
echo 'export PATH=" $HOME/.cargo/bin:$PATH"' >> ~/ .zshrc

2.2 Rustの基本

2.2.1 文字出力とコメント

コードを表示
fn main() {
    //一行の時は「//」
    println!("Hello word!"); //改行あり
    print!("Hello"); //改行しない
}
出力
Hello World!
Hello%

2.2.2 変数宣言と定数

コードを表示
const A: i32 = 1; //定数はconstのみ
fn main() {
    println!("{}", A);
    let a: i32 = 20;
    println!("{}",a); //出力はプレースフォルダーを使用

    let mut b: i32 = 1; //値の変更をしたい場合は「mut」をつける
    println!("{}",b);
    b = 2;
    println!("{}",b);
    let b: &str = "test"; //シャドウイングで文字の型が変わる場合は「&」が必須
    println!("{}", b);
}
出力
1
20
1
2
test

2.2.3 数値型と論理型

アルファベット サイズ
i(符号付き整数) 8, 16, 32, 64, 128, size i32, isize
u(符号なし整数) 8, 16, 32, 64, 128, size u32, usize
u(浮動小数点数) 32, 64 f32, f64
コードを表示
fn main() {
    let a: i32 = (2.1 * 4.0) as i32; //キャスト
    println!("{}",a);
    let b:bool = true; //論理型
    println!("{}", b);
    let b:bool = 1 == 2; //等価比較
    println!("{}", b);
    let b:bool = 1 != 2; //非等価比較
    println!("{}", b);
    let b:bool = 1 > 2; //大なり
    println!("{}", b);
    let b = 1 < 2; //小なり
    println!("{}", b);
    let b = 1 >= 2; //大なりイコール
    println!("{}", b);
    let b = 1 <= 2; //小なりイコール
    println!("{}", b);
}
出力
8
true
false
true
false
true
false
true

2.2.4 タプル,配列及びベクタ

コードを表示
fn main() {
    let t1:(i32, bool, f64)  = (1, true, 2.0); //タプル
    let t2 = (2.0, 1, true); //t1 != t2
    println!("{:?}", t1);
    let i:i32 = t1.0; //0番目を取得
    println!("{}", i);
    let (x, y, _) = t2; //全て取得
    println!("{}", x);

    let l1: [i32; 3]  = [1, 2, 3]; //配列
    println!("{:?}", l1);
    let i:i32 = l1[0]; //0番目を取得
    println!("{}", i);
    let [x, y, z] = l1; //全て取得
    println!("{}", y);
    let l2: &[i32] = &l1[0..2]; //indexの指定
    println!("{:?}", l2);

    let v1: Vec<i32> = vec![1,2,3]; //ベクタ
    let v2: Vec<i32> = vec![0; 100]; //0の値が100個作られる
    println!("{:?}", v1);

    let mut v3: Vec<i32> = Vec::new(); //値を変更できるようにmutをつける
    v3.push(1);
    v3.push(100);
    v3.push(-10);
    println!("{:?}", v3);
    v3.pop();
    println!("{:?}", v3);
}
出力
(1, true, 2.0)
1
2
[1, 2, 3]
1
2
[1, 2]
[1, 2, 3]
[1, 100, -10]
[1, 100]

配列とベクタの違いは、値を後から変更できるかどうか

2.2.5 文字型と文字列型

コードを表示
fn main() {
    //文字型
    let c1:char = 'a';
    let c2:char = '@';
    println!("{} {}", c1, c2);

    //文字列型
    let s1: &str = "Rust"; //変更不可
    let mut s2: String = String::from("Python"); 
    let mut s3: String = "Java".to_string();
    s3.push_str(" Hello");
    println!("{} {} {}", s1, s2, s3);
}
出力
a @
Rust Python Java Hello

2.3 フロー制御

2.3.1 if文

コードを表示
fn main() {
    let x = 5;
    let y = 1;
    if x > 0 {
        println!("OK!");
    }
    if x>=0 && x<=10 {
        println!("0以上10以下");
    }
    if x>0 || y==2 {
        println!("0より上または、y=1");
    }
    if x <0{
        println!("0より下");
    }else if x < 5{
        println!("0以上5より下")
    }else{
        println!("5以上");
    }
    
    let z = if x > 10 { //ifを使用して値を入力した場合は、ifは何らかの値を返すかつ同じ型
        x
    }else{
        0
    };
    println!("{}", z);
}
出力
OK!
0以上10以下
0より上または、y=1
5以上
0

2.3.2 match

コードを表示
fn main() {
    let x = 1;
    match x{
        0 => println!("Zero"),
        1 => {
            println!("a");
            println!("b");
        },
        _ => println!("other"), //elseと同じ
    };

    let y = match x{ //値を代入したい時
        0 => 0,
        1 => 10,
        _ => 100,
    };
    println!("{}", y);
}
出力
a
b
10

matchは、switchに近い

2.3.3 ループ処理 (loop, while, for)

コードを表示
fn main() {
    let mut cnt = 0;
    loop { //無限に繰り返す
        if cnt == 3{
            break;
        }
        println!("{}", cnt);
        cnt += 1;
    }
    cnt = 0;
    while cnt <= 4 { //条件付きで繰り返す
        println!("{}", cnt);
        cnt += 1;
    }
    for i in 0..2 { //ある範囲の間で繰り返す
        println!("Hello {}", i);
    }
}   
出力
0
1
2
0
1
2
3
4
Hello 0
Hello 1

2.4 所有権と参照

2.4.1 スタックとヒープ

スタック領域について

  • スタック領域とは、一時的なデータ(ローカル変数, 関数の引数等)格納
  • Last In Fitst Out(LIO)のシンプルな構造
  • アクセスが高速
  • 格納できる容量が限られている (Rustでは8MB)

LIFOについて

  • 先入れ後出しの考え方
  • 3つの関数(funcA, funcB, funcC)があったとき、funcA->funcB->funcCの順番で格納する。解放するときは、funcC->funcB->funcAの順番で解放する

ヒープ領域について

  • ヒープ領域とは、動的なデータを格納する
  • スタックと比べてアクセスが低速

2.4.2 所有権

所有権とは

  • Rustがメモリ管理のために採用している仕組み
  • メモリ上に存在する値を変数が所有するという考え方
  • 所有権は移動や借用などが行われる

所有権のルール

  • Rustの各値は、所有権と呼ばれる変数に対応している
  • いかなる時も所有権は一つである
  • 所有権がスコープを外れたら、値は破棄される
コードを表示
fn main() {
    let a = 100; //変数aがメモリ上の値(100)の所有権を得る
    {
        let mut v1 = vec![1, 2, 3];
        println!("{:?}", v1);

        let v2 = v1; //所有権の移動
        println!("{:?}", v2);

        //v1.push(3); 所有権の移動が行っているのでエラー
        
    }//スコープを抜けたのでv2が所有するメモリ上の値を破棄
    println!{"{}", a};
}//変数aの値を破棄
出力
[1, 2, 3]
[1, 2, 3]
100

所有権の移動の例外

  • cloneメソッドを利用すると所有権が移動しないv2 = v1.clone();
  • 数値型や論理型などはコピーが行われて所有権の移動を行わない

2.4.3 参照

参照とは

  • 所有権を持たないポインタ
  • 変数に&または&mutをつけることによって作成される
  • ある値の参照を作ることを借用するという
  • 参照に*をつけることで、参照の実態にアクセス可能 (参照外し)

参照の種類

  • 共有参照
  • 可変参照

共有参照

  • 参照先を読むことができるが、変更は不可
  • 変数に&をつけることで作成
  • 同時に複数作成可能

可変参照

  • 値の読み出しと変更が可能
  • 変数に&mutをつけることで作成
  • ある値に可変参照が存在する場合、その他の参照は作成不可
コードを表示
fn main() {
    //参照は使わない例
    let s1:String = String::from("Hello");
    let s2:String = String::from("Rust");
    let (s, s1, s2)= concat(s1, s2);
    println!("{}", s);

    //共有参照を使用した関数の例
    let s_reference = concat_reference(&s1, &s2);
    println!("{}", s_reference);

}
fn concat(a:String, b:String) -> (String, String, String) {
    let c:String = format!("{} {}", a, b);
    (c, a, b)
}
fn concat_reference(a: &String, b: &String) -> String{
    let c:String = format!("{} {}", a, b);
    c
}
出力
Hello Rust
Hello Rust

2.4.4 ライフタイム

ライフタイムとは

  • 参照が有効になるスコープ
  • ライフタイムは推論によって省略される
  • 省略せずに書く場合は、ライフタイムパラメータを'aのように書く(tick Aと呼ぶ)
コードを表示
fn main() {
    let r: &i32;
    {
        let x: i32 = 1;
        r = &x;
    }
    println!("{}", r); //変数rは、xの参照である。xのスコープは、この時点は抜けているためエラーが発生する。
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { //xとyのライフタイムが同じであることを示している
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
出力
error[E0597]: `x` does not live long enough

2.4.5 スマートポインタ

  • スマートポインタは、参照に特別な機能を持たしたもの
  • Box, Rc, Arcなどがあるが今回はBoxについてコードを書く
コードを表示
use std::rc::Rc;
fn main() {
    let r;
    {
        let x: Box<i32> = Box::new(1);
        r = x;
    } // xのスコープが終了しても、ヒープ上のデータは有効
    println!("{}", r); // これでエラーは発生しない
    
    //Boxには単一の所有権のため参照カウントがないのでRcで参照カウント表示
    let a = Rc::new(1);
    println!("参照カウント: {}", Rc::strong_count(&a));

    {
        let b = Rc::clone(&a);
        println!("参照カウント: {}", Rc::strong_count(&a));
    } // bがスコープを抜けると、参照カウントが減少

    println!("参照カウント: {}", Rc::strong_count(&a));
}
出力
1
参照カウント: 1
参照カウント: 2
参照カウント: 1

基本的なデータは、スタック領域に格納されるためその変数がスコープを抜けるとメモリが解放され値は失われる。スマートポインタを用いるとヒープ領域に格納されるため、変数がスコープを抜けても所有権が他の変数に移動していた場合、ヒープ領域に保持される

2.5 構造体と列挙型

2.5.1 構造体について

コードを表示
struct Rectangle { //構造体を作成
    width: i32,
    height: i32,
}

impl Rectangle {
    fn area(&self) -> i32 { //構造体のメソッド定義 引数にselfが必要(所有権が渡ってしまうから共有参照にする)
        self.width * self.height
    }
}

fn main() {
    let height = 33;
    let mut rectangle: Rectangle = Rectangle{  // mutで可変にも可能
        width: 10,
        height, //省略型
    };
    println!("{} {}", rectangle.width, rectangle.height);

    rectangle.height = 5; //値を変更
    println!("{} {}", rectangle.width, rectangle.height);

    println!("{}", rectangle.area()); //構造体のメソッドの呼び出し
}
出力
10 33
10 5
50

2.5.2 型関連関数

コードを表示
struct Texts {
    s1: String,
    s2: String,
}

impl Texts {
    fn concat(&self) -> String {
        let s: String = format!("{}_123_{}", self.s1, self.s2);
        s
    }
    fn new (s1: String, s2: String) -> Self { // 型関連関数
        Texts { s1, s2 }
    }
}

fn main() {
    let s1: String = "hello".to_string();
    let texts: Texts = Texts::new(s1, "world!".to_string());
    // println!("{}", s1); //所有権が移動したのでエラー
    println!("{}", texts.concat())
}
出力
hello_123_world!

型関連関数は、インスタンスの初期化を簡潔に行える

2.5.2 列挙型について

コードを表示
enum Shape { //列挙型
    Circle, //バリアント
    Square(u32), //タプル型バリアンと
    Triangle{base: u32, height: u32}, //構造体バリアント
}

impl Shape {
    fn sample_method(&self) { //メソッドの定義
        println!("call sample");
    }
}
fn main() {
    let c: Shape = Shape::Circle;
    let s: Shape = Shape::Square(1);
    let t: Shape = Shape::Triangle {base: 10, height: 5};
    c.sample_method();
    s.sample_method();
    t.sample_method();
}

出力
call sample
call sample
call sample

2.5.3 Option型

コードを表示
fn main() {
    // enum Option<T> { 列挙型で有名のOption型、標準機能として備わっている
    //     None,
    //     Some(T),
    // }
    let a: Option<i32> = Some(1); 
    let b: Option<&str> = Some("str");
    let c: Option<u32> = None;
    println!("{:?}", c);

    let v: Vec<i32> = vec![1, 2, 3];
    let val = v.get(2);
    println!("{:?}", val);

    match val { //None必須
        Some(x) => println!("{}", x),
        None => println!("None"),
    }
    if let Some(x) = val { //ifの時はNoneがいらない
        println!("{}", x);
    }
}

出力
None
Some(3)
3
3

2.6 クレートとモジュール

2.6.1 クレート

クレートとは

  • Rustのプログラムを構成する要素
  • ライブラリもしくは実行ファイルのソースコード、関連するテスト、ツールなど全てが収められる
  • クレートには実行が可能なバイナリクレート、バイナリクレートから機能として呼び出されるライブラリクレートがある

パッケージ

  • ある機能群を提供する1つ以上の集合
  • cargo new プロジェクト名で作成でき、Cargo.tomlにパッケージ情報を記述
  • パッケージに含むことができるクレート数は、ライブラリクレートが0または1個、バイナリクレートが0個以上

クレートの依存関係

  • あるクレートから別のクレートを呼び出して使用可能
  • crates.ioから他者のクレートを取得したり自分のクレートを公開可能
  • crates.ioからクレートを取得するには、Cargo.tomlの[dependencies]セクションに使用したいクレートとそのバージョンを指定
コードを表示
main.rs
use rand::Rng;//randの中のRngを使用する
fn main() {
    let random_number = rand::thread_rng().gen_range(1..10);
    println!("{}", random_number)
}
Cargo.toml
[dependencies]
rand = "0.8.5"
出力
4

2.6.2 モジュール

モジュールとは

  • クレート内のコードをグループ化し、可読性と再利用性を高める機構 (関数, 構造体, 列挙型, 定数などが収められる)
  • modキーワードを使用
  • 1ファイル1モジュールが基本

モジュールの可読性

  • モジュール内要素の可視性を制御可能
  • publicは、外部からアクセス可能
  • privateは、モジュール内もしくはその子モジュールのみからアクセス可能
  • デフォルトは全てprivate
コードを表示
mod test_module{
    pub fn test_fn1() { //public
        println!("Hello world!");
    }
    fn test_fn2(){ //private
        println!("Hello Rust!");
    }
}

モジュールのネスト

  • モジュールはネストすることができる (ネストされた内側のモジュールをサブモジュールと呼ぶ)
  • ネストされたモジュールの要素を外部から呼ぶ出すときは、その要素とサブモジュールもpublicにしないといけない
  • pub(super)とすると、親モジュールからのみアクセス可能

パス

  • モジュールを使用するには、パスが必要
  • 絶対パスは、crateから始まる
  • 相対パスは、self、superなどを使用して、現在のモジュールから始める

インポート

  • 使いたいモジュールの要素をインポートすることでパスを省略することが可能
  • インポートにはuseキーワードを使用する
コードを表示
mod test_module{
    pub mod sub_module1{
        pub fn test_fn1(){
            println!("Hello world!");
        }
        fn test_fn2(){
            println!("Hello Rust!");
        }
    }

    mod sub_module2{
        pub fn test_fn1(){
            println!("Hello world!");
        }
        fn test_fn2(){
            println!("Hello Rust!");
        }
    }

}
use test_module::sub_module1; //import
fn main() {
    crate::test_module::sub_module1::test_fn1(); //絶対パス
    self::test_module::sub_module1::test_fn1(); //相対パス 今回は、main.rsからの相対パスなどで絶対パスと同じ。またselfは省略可
    // self::test_module::sub_module2::test_fn1(); privateなどでエラー
    sub_module1::test_fn1();

}
出力
Hello world!
Hello world!
Hello world!

モジュールのファイル分割

コードを表示
src/test_modules/sub_module1.rs
pub fn test_fn1(){
    println!("Hello world!");
}
fn test_fn2(){
    println!("Hello Rust!");
}
src/test_modules.rs
pub mod sub_module1;
src/main.rs
mod test_module;
use test_module::sub_module1;
fn main() {
    sub_module1::test_fn1();
}
出力
Hello world!

また、src/test_module.rssrc/test_module/mod.rsにしても良い

構造体の可視性

src/test_modules/sub_module1.rs
pub struct TestStruct{ //構造体を変数までpubをつける
    pub val1: i32,
    pub val2: i32,
}

impl TestStruct{ //型関連関数を使用する
    pub fn new(val1:i32, val2: i32) -> TestStruct{
        TestStruct{val1, val2}
    }
}
src/main.rs
mod test_module;
use test_module::sub_module1;
fn main() {
    let A = sub_module1::TestStruct{val1: 1, val2: 2};
    println!("{}", A.val2);
    let TestStruct = sub_module1::TestStruct::new(1, 2);
    println!("{}", TestStruct.val1);
}
出力
2
1

2.6.3 バイナリとライブラリ

これまで行なってきたことはmain.rsで実行したものである。
他のコードから実行する方法

コードを表示
src/bin/bin1.rs
fn main() {
    println!("bin1.rs");
    rust_lesson::say_hello(); //ライブラクレート
}
src/lib.rs
pub fn say_hello(){
    println!("Hello");
}
terminal
cargo run --bin bin1 
出力
bin1.rs
Hello

main.rsも同じようにCargo.tomlに書かれている名前を使用すれば行える

terminal
cargo run --bin Cargo.tomlの名前 
出力
main.rs
Hello

2.6.4 ドキュメント

このようなドキュメントを作成する
スクリーンショット 2024-08-10 13.51.41.png

terminal
cargo doc --no-deps --open
  • docは、ドキュメント作成
  • --no-depsは、Cargo.tomldependenciesを書かないようにしている
  • --openは、ブラウザを開く

ドキュメンの中身について

-- クレートごとに表示される
-- publicしか表示されない
-- コメントすることができる
-- ライブラリコメント://!
-- ドキュメントコメント:///

2.6.5 ビルドプロファイル

本番運用時に最適化する方法
これにより実行速度が上げられる

terminal
cargo run --bin Cargo.tomlの名前 --release
  • target/relaseに格納される
  • Cargo.tomlに設定することで、最適化レベルを変更できる
  • 開発用の初期値は0, 本番用の初期値は3となっており、0〜3の整数値が取れる
  • 値が大きくなるにつれてコンパイルの最適化が行われる
Cargo.toml
[profile.dev]
opt-level = 1

[profile.release]
opt-level = 1

2.7 トレイトとジェネリクス

2.7.1 トレイト

トレイトとは

  • 型が特定の機能を持っていることを保証する機能
  • 異なる型に共通の振る舞いを定義することができ、実装することができる
コードを表示
lib.rs
pub mod sample_trait {
    pub trait Shape {
        //トレイトを定義
        fn calc_area(&self) -> f64;
        fn calc_perimeter(&self) -> f64;
        fn default_something(&self) -> &str { //デフォルト実装も可、オーバーライドすることも可
            "This is default method!"
        }
    }

    pub struct Rectangle {
        pub width: f64,
        pub height: f64,
    } //構造体

    impl Shape for Rectangle {
        //トレイトの実装
        fn calc_area(&self) -> f64 {
            self.width * self.height
        }
        fn calc_perimeter(&self) -> f64 {
            self.width * 2.0 + self.height * 2.0
        }
    }
    
    pub fn double_area(shape: &impl Shape) ->f64 { //引数の型としてのトレイト実装, Shapeトレイトを実装している型であればどの型でも可能
        shape.calc_area() * 2.0
    }
}
main.rs
use rust_lesson::sample_trait::{Shape, Rectangle, double_area};

fn main() {
   let rect = Rectangle {width:4.0, height: 5.0};
   println!("{}", rect.calc_area());
   println!("{}", rect.calc_perimeter());
   println!("{}", rect.default_something());
   println!("{}", double_area(&rect));
}
出力
20
18
This is default method!
40

2.7.2 Derive属性による継承

コードを表示
fn main() {
    #[derive(Debug, PartialEq)] //特定のトレイを自動的に実装するために使用
    struct S{
        val1: i32,
        val2: i32,
    }
    println!("{:?}", S {val1:10, val2:4});

    let s1 = S {val1: 1, val2: 2};
    let s2 = S {val1: 1, val2: 3};
    println!("{}", s1==s2);
}
出力
S { val1: 10, val2: 4 }
false

2.7.3 ジェネリクス

ジェネリクスとは

  • 特定のデータ型に依存せず、異なるデータ型でも再利用が行える
コードを表示
//use std::fmt::Debug;

fn max<T: PartialOrd>(a: T, b: T) -> T
// where T: PartialOrd + Debug どちらでも行えるが括弧の中身が長くなる時に使用
{
   
    if a>=b{
        a
    }else{
        b
    }
}
fn main() {
    println!("{}", max(1, 2));
    println!("{}", max(1.1, 2.3));
    println!("{}", max("a", "b"));
}
出力
2
2.3
b

ジェネリックスの構造体

コードを表示
use std::fmt::{Debug, Display};

struct Point<T> {
    x: T,
    y: T,
}

impl <T: PartialOrd + Debug> Point<T> {
    fn max(&self) -> &T {
        if self.x >= self.y {
            &self.x
        }else{
            &self.y
        }
    }
    fn print_arg<U: Display>(&self, val: U){
        println!("self.x {:?}", self.x);
        println!("val {}", val);
    }
}
fn main() {
    let p1 = Point {x: 1, y:2};
    println!{"{:?}", p1.max()};
    p1.print_arg("test");
    p1.print_arg(true);
}
出力
2
self.x 1
val test
self.x 1
val true

2.8 エラー処理

2.8.1 パニック

コードを表示
fn main() {
    //println!("{}", 1/0); //0で割ったのでエラー
    panic!("This is my panic!"); //自分でpanicを発生することも可能
}

基本的にはプログラマー側のエラー

2.8.2 Reuslt型

コードを表示
// pub enum Result <T, E> { Result型は列挙型でこのように定義されている
//     Ok(T),
//     Err(E),
// }

fn need_even(a: i32) -> Result<i32, String> {
    if a % 2 == 0{
        Ok(a)
    }else{
        Err(String::from("奇数"))
    }
}

fn main() {
    println!("{:?}", need_even(1));
    println!("{:?}", need_even(2));
}
出力
Err("奇数")
Ok(2)

2.8.3 エラーハンドリング

コードを表示
fn need_even(a: i32) -> Result<i32, String> {
    if a % 2 == 0{
        Ok(a)
    }else{
        Err(String::from("奇数"))
    }
}

fn main() {
    let x = match need_even(2) {
        Ok(val) => val, //Ok
        Err(err) => { //Err
            println!("Error message {}", err);
            panic!(); //panic
        }
    };
    println!("{}", x); //エラーが起きた場合出力されない

    let s = need_even(1);
    println!("{}", s.is_ok()); //Okの場合True
    println!("{}", s.is_err()); //Errの場合True
    println!("{}", s.unwrap_or(0)); //Err場合括弧の中身を出力
    //println!("{}", s.unwrap()); //Errの場合panicを起こす
    // println!("{}", s.expect("expect")); //Errの場合expectを起こす
}
出力
2
false
true
0
所有権の移動が起きるのでコードを実行するときは、一つずつ確認して下さい

2.8.4 エラーの委譲

コードを表示
fn need_even(a: i32) -> Result<i32, String> {
    if a % 2 == 0{
        Ok(a)
    }else{
        Err(String::from("奇数"))
    }
}

fn double_even (b:i32) -> Result<i32, String> {
    let x = need_even(b)?;
    Ok(x * 2)
}

fn main() {
    match double_even(1) {
        Ok(val) => println!("{}", val),
        Err(err) => { //Err
            println!("mainでハンドリング");
            println!("{}", err);
        }
    };

}
出力
mainでハンドリング
奇数

2.9 クロージャーとイテレータ

2.9.1 クロージャーとは

  • 変数に束縛できたり,関数の引数として渡すことのできる名前のない関数(無名関数)のこと
コードを表示
 fn main() {
    let c1 = |x: i32| x + 1; //||で宣言可能, ||の中に引数をとる
    println!("{:?}", c1(10)); //関数のように使用する

    let m = 10;
    let c2 = |x: i32| x + m; //クロージャの外にある値を使用可能
    println!("{:?}", c2(10));

    let m = 20;
    println!("{:?}", c2(10));

    let v = vec![1, 2, 3];
    let c3 = move || { //一般的には参照を渡しているが移動させたい場合
        println!("{:?}", v)
    };
    c3();
    //println!("{:?}", v); 所有権を持っていないのエラー
}
出力
11
20
20
[1, 2, 3]

2.9.2 イテレータとは

  • 連続したオブジェクトを順番に取り扱うための機能を提供するオブジェクト
コードを表示
fn main() {
    let v = vec![1, 2, 3];
    let v1_iter = v.iter();
    for x in v1_iter{
        println!("{:?}",x);
    }

    let mut v2_iter = v.iter();
    println!("{:?}", v2_iter.next()); 
    println!("{:?}", v2_iter.next());
    println!("{:?}", v2_iter.next());
    println!("{:?}", v2_iter.next());
}
出力
1
2
3
Some(1)
Some(2)
Some(3)
None

2.9.3 独自のイテレータ

コードを表示
struct Counter {
    start: u32,
    end: u32,
}

impl Iterator for Counter {
    type Item = u32;
    
    fn next(&mut self) -> Option<u32> {
        if self.start > self.end {
            None
        }else{
            let result = Some(self.start);
            self.start += 1;
            result
        }
    }
}

fn main() {
    let mut c = Counter{start: 1, end: 3};
    for i in c {
        println!("{}", i)
    }
    //println!("{:?}", c.next()); //nextを用いたてSome(1)のように出力することも可能

}

1
2
3

出力

2.9.4 イテレータのメソッド

コードを表示
fn main() {
    let v = vec![1, 2, 3, 4, 5];
    let m = v.iter().map(|x: &i32| x * 2);// mapメソッド: 各要素に関数を適用
    for val in m {
        println!("{}", val);
    }
    let c: Vec<_> = v.iter().map(|x: &i32| x * 2).collect();
    println!("{:?}", c);

    let f: Vec<_> = v.iter().filter(|x: &&i32| * x % 2 != 0).collect(); //filterメソッド: trueを返したメソッドだけ取り出す
    println!("{:?}", f);

    let v_count = v.iter().count(); //count
    println!("{:?}", v_count);
    
    let v_sum: i32 = v.iter().sum();
    let v_product: i32 = v.iter().product();
    println!("{:?}", v_sum);
    println!("{:?}", v_product);

    let max: Option<&i32> = v.iter().max();
    let min: Option<&i32> = v.iter().min(); 
    println!("{:?}", max);
    println!("{:?}", min);

    let s2: i32 = v.iter().fold(0, |sum: i32, x: &i32| sum + x); //foldメソッド: 各要素に対して関数を適用して状態を更新し、その状態を返す
    println!("{}", s2);

}
出力
2
4
6
8
10
[2, 4, 6, 8, 10]
[1, 3, 5]
5
15
120
Some(5)
Some(1)
15

2.10 コレクション

2.10.1 ベクタ

コードを表示
fn main() {
    let mut v1 = vec!["PHP", "Python", "RUST"];
    v1.insert(1, "Java"); //indexを指定して追加
    println!("{:?}", v1);
    v1.remove(3); //indexを追加して削除
    println!("{:?}", v1);

    let v2 = vec!["Go", "Java"];
    let v3 = [v1, v2].concat(); //連結
    println!("{:?}", v3);

    let (v4, v5) = v3.split_at(2); //indexを指定して分割
    println!("{:?}", v4);
    println!("{:?}", v5);

    let mut v6 = vec![10, 1, 4];
    v6.sort(); //並び替え
    println!("{:?}", v6);
    v6.reverse(); //上から並び替え
    println!("{:?}", v6);

    #[derive(Debug)]
    struct S {
        val1: i32,
        val2: i32,
    }
    
    let mut v7: Vec<S> = vec![
        S {val1: 3, val2: 1},
        S {val1: 2, val2: 2},
        S {val1: 1, val2: 3},
    ];
    v7.sort_by_key(|s| s.val1); //val1を基準とした並び替え
    println!("{:?}", v7);

    println!("{:?}", v6.contains(&10)); //10が含んでるかどうか。なかったらNone

    let x = v6.iter().position(|x| *x == 10); //ある場合indexがSome(index)が返される。なかったらNone
    println!("{:?}", x);
}
出力
["PHP", "Java", "Python", "RUST"]
["PHP", "Java", "Python"]
["PHP", "Java", "Python", "Go", "Java"]
["PHP", "Java"]
["Python", "Go", "Java"]
[1, 4, 10]
[10, 4, 1]
[S { val1: 1, val2: 3 }, S { val1: 2, val2: 2 }, S { val1: 3, val2: 1 }]
true
Some(0)

2.10.2 キュー

コードを表示
use std::collections::{VecDeque, BinaryHeap};
fn main() {
    let mut q: VecDeque<i32> = VecDeque::new(); //キューの初期化
    //let mut g = VecDeque::from(vec![1, 2, 3]); //初期値が決まってる場合
    q.push_back(1); //一番後ろに追加
    q.push_back(2);
    q.push_back(3);
    println!("{:?}", q);
    println!("{:?}", q.pop_front()); //一番前を削除
    println!("{:?}", q);

    let mut bh: BinaryHeap<i32> = BinaryHeap::new(); //バイナリーヒープは、最大の要素が先頭に来る
    bh.push(1);
    bh.push(10);
    bh.push(3);
    println!("{:?}", bh);
    println!("{:?}", bh.pop());
    println!("{:?}", bh);
}
出力
[1, 2, 3]
Some(1)
[2, 3]
[10, 1, 3]
Some(10)
[3, 1]

2.10.3 マップ

コードを表示
use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new(); //マップ初期化
    map.insert("Japan", 1); //追加
    map.insert("China", 5);
    map.insert("America", 2);
    println!("{:?}", map);

    map.insert("Japan", 3); //同じkeyの場合valueが更新
    println!("{:?}", map);

    println!("{:?}", map.get("Japan")); //valueを取得
    println!("{:?}", map.remove("China")); //削除
    println!("{:?}", map);

    for (k, v) in &map{ 
        println!("{:?}: {:?}", k, v);
    }
}
出力
{"Japan": 1, "China": 5, "America": 2}
{"China": 5, "America": 2, "Japan": 3}
Some(3)
Some(5)
{"America": 2, "Japan": 3}
"America": 2
"Japan": 3

2.10.4 セット

コードを表示
use std::collections::HashSet;

fn main() {
    let mut set1 = HashSet::new(); //セットの宣言
    set1.insert(1); //追加, 同じ値は一つしか入れれない
    set1.insert(1);
    set1.insert(2);
    println!("{:?}", set1); //順番は適当

    println!("{:?}", set1.contains(&2)); //含んでいるかどうか
    println!("{:?}", set1.remove(&2)); //削除
    println!("{:?}", set1); 

}
出力
{1, 2}
true
true
{1}
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?