技術共有

Rust プログラミング言語の学習 - 関数型言語の機能: イテレータとクロージャ

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

さび クロージャは、変数に保存したり、引数として他の関数に渡すことができる匿名関数です。 1 つの場所でクロージャを作成し、別のコンテキストでクロージャ操作を実行できます。関数とは異なり、クロージャでは、定義されているスコープ内の値をキャプチャできます。

イテレータは、シーケンス内の各項目を走査し、シーケンスがいつ終了するかを決定するロジックを担当します。イテレータを使用する場合、このロジックを再実装する必要はありません。

1. クロージング

クロージャ。変数に格納できる関数のような構造。

1.1 クロージャは環境をキャプチャします

Rust では、クロージャは外部環境から変数をキャプチャする匿名関数です。クロージャのキャプチャ動作は、変数のタイプとクロージャがそれらの変数をどのように使用するかによって異なります。

値によるキャプチャ (By Value)

クロージャが値によって変数をキャプチャすると、変数の所有権が取得されます。これは、クロージャが作成された後は、元の変数は使用できなくなることを意味します。

fn main() {
    let text = "Hello".to_string();
    // 使用 move 来显式地表示闭包将获取 text 的所有权
    let closure = move || println!("{}", text);
    // 这里 text 不能被使用,因为其所有权已经被闭包获取
    // println!("{}", text); // 这将导致编译错误
    closure(); // 打印 "Hello"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

参照によるキャプチャ (By Reference)

クロージャが参照によって変数をキャプチャする場合、その変数を借用します。これは、元の変数はまだ利用可能ですが、クロージャはそれを借用することしかできず、所有権を取得できないことを意味します。

fn main() {
    let text = "Hello";
    // 闭包通过引用捕获 text
    let closure = || println!("{}", text);
    // text 仍然可用,因为它没有被移动
    println!("{}", text); // 打印 "Hello"
    closure(); // 再次打印 "Hello"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可変キャプチャ

クロージャは可変参照をキャプチャして、元の変数の値を変更できるようにします。

fn main() {
    let mut count = 0;
    // 闭包通过可变引用捕获 count
    let mut closure = || {
        count += 1; // 修改 count 的值
        println!("Count: {}", count);
    };
    closure(); // 打印 "Count: 1"
    closure(); // 打印 "Count: 2"
    // count 的值现在是 2
    println!("Final count: {}", count);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

クロージャーが外された場合 mut 変更、コンパイルに失敗します。

fn main() {
    let mut count = 0;
    // 闭包通过可变引用捕获 count
    let closure = || {
        count += 1; // 修改 count 的值
        println!("Count: {}", count);
    };
    closure(); // 打印 "Count: 1"
    closure(); // 打印 "Count: 2"
    // count 的值现在是 2
    println!("Final count: {}", count);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

エラー メッセージ プロンプト: 変数を借用しているため count、移行 closure 可変バインディングが必要です。

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `closure` as mutable, as it is not declared as mutable
 --> src/main.rs:4:9
  |
4 |     let closure = || {
  |         ^^^^^^^ not mutable
5 |         count += 1; // 修改 count 的值
  |         ----- calling `closure` requires mutable binding due to mutable borrow of `count`
...
8 |     closure(); // 打印 "Count: 1"
  |     ------- cannot borrow as mutable
9 |     closure(); // 打印 "Count: 2"
  |     ------- cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
4 |     let mut closure = || {
  |         +++

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` (bin "playground") due to 1 previous error
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

引数としてのクロージャ

クロージャーは次のように使用できます。パラメータの受け渡し他の関数に対して、環境内の変数をキャプチャします。

fn main() {
    // 创建一个整数变量
    let number = 10;

    // 创建一个闭包,它接受一个 i32 类型的参数并返回其平方
    // 这里使用 || 表示这是一个闭包
    let square = || number * number;

    // 定义一个函数,它接受一个闭包作为参数并调用它
    // 闭包作为参数需要指定其类型,这里使用 || -> i32 表示闭包没有参数并返回 i32 类型的值
    fn call_closure<F>(f: F)
    where
        F: Fn() -> i32, // 使用 trait bound 指定闭包的签名
    {
        // 调用闭包并打印结果
        let result = f();
        println!("The result is: {}", result);
    }

    // 调用 `call_closure` 函数,并将闭包 `square` 作为参数传递
    // 由于闭包 `square` 没有参数,我们可以直接传递
    call_closure(square);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

この例では:

  1. クロージャを定義します square、参照によってキャプチャします main 機能中number 変数を使用してその二乗を計算します。
  2. というファイルを定義します。 call_closure を受け入れる関数。Fn() -> i32 特性バインドされたクロージャはパラメータを受け取ります。つまり、クロージャはパラメータを受け取らず、i32 値のタイプ。
  3. 私たちは電話します call_closure 機能と意志square クロージャは引数として渡されます。なぜならsquare クロージャにはパラメータがありません。クロージャはパラメータとして直接渡すことができます。call_closure
  4. call_closure この関数はクロージャを呼び出し、結果を出力します。

1.2 クロージャ型の推論とアノテーション

関数とクロージャの間にはさらに多くの違いがあります。クロージャは常に次のようなものを必要とするわけではありません fn パラメータの種類を指定し、関数と同様に値を返します。関数はユーザーに公開される明示的なインターフェイスの一部であるため、関数には型アノテーションが必要です。これらのインターフェイスを厳密に定義して、使用される関数と戻り値の型を全員が一貫して理解できるようにすることが重要です。対照的に、クロージャはそのような公開されたインターフェイスには使用されません。クロージャは変数に格納され、名前を付けたり、ライブラリのユーザーに公開したりすることなく使用されます。

クロージャは通常短く、任意の状況ではなく狭いコンテキストに関連しています。これらの制限されたコンテキストでは、コンパイラーは、ほとんどの変数の型を推論する方法と同様に、パラメーターと戻り値の型を確実に推論できます (ただし、コンパイラーがクロージャ型の注釈を必要とする場合はまれにあります)。

変数と同様に、明確さと明確さを高めたい場合は、型アノテーションを追加できますが、(厳密に必要ではなく) コードがより冗長になるという欠点があります。

fn main() {
    let a = 100;
    let add_one = |x: i32| -> i32 { x + 1 };
    let b = add_one(a);
    println!("{}", b); 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

型アノテーションを使用すると、クロージャの構文は関数により似たものになります。以下は、引数に 1 を追加する関数の定義と、同じ動作をするクロージャ構文の垂直比較です。対応するパーツを配置するために、ここではいくつかのスペースが追加されています。これは、パイプといくつかのオプションの構文の使用を除いて、クロージャ構文が関数構文とどのように似ているかを示しています。

fn  add_one_v1   (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x|             { x + 1 };
let add_one_v4 = |x|               x + 1  ;
  • 1
  • 2
  • 3
  • 4

したがって、上記の例は次のように簡略化できます。

fn main() {
    let a = 100;
    let add_one = |x| x + 1;
    let b = add_one(a);
    println!("{}", b); 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

コンパイラは、クロージャ定義内の各パラメータと戻り値の具象型を推論します。

別の例を見てみましょう。

fn main() {
    let a = 100i32;
    let a1 = 100f32;
    let closure = |x| x;
    let b = closure(a);
    let b1 = closure(a1);
    println!("{}", b); 
    println!("{}", b1); 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

このクロージャ定義は型アノテーションを追加しないため、このクロージャを任意の型で呼び出すことができることに注意してください。ただし、クロージャを 2 回呼び出そうとすると、1 回目は i32 を使用し、2 回目は f32 を使用して、エラーが報告されます。クロージャは以前に「i32」型の引数を使用して呼び出されているため、予期されています。

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:6:22
  |
6 |     let b1 = closure(a1);
  |              ------- ^^ expected `i32`, found `f32`
  |              |
  |              arguments to this function are incorrect
  |
note: expected because the closure was earlier called with an argument of type `i32`
 --> src/main.rs:5:21
  |
5 |     let b = closure(a);
  |             ------- ^ expected because this argument is of type `i32`
  |             |
  |             in this closure call
note: closure parameter defined here
 --> src/main.rs:4:20
  |
4 |     let closure = |x| x;
  |                    ^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 1 previous error
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

1.3 キャプチャされた値をクロージャと Fn トレイトから移動する

クロージャが、それが定義されている環境内の値への参照または所有権を取得すると (したがって、クロージャに移動されるものがあればそれに影響します)、クロージャ本体内のコードは、後でクロージャが定義されるときにその参照を定義します。または、値がどのように操作されるか (クロージャから削除されるものがあればそれに影響します)。クロージャ本体は、キャプチャされた値をクロージャの外に移動する、キャプチャされた値を変更する、値の移動も変更も行わない、またはそもそも環境から値をキャプチャしないことのいずれかを行うことができます。

クロージャが環境内の値をキャプチャして処理する方法は、クロージャが実装する特性に影響します。 トレイトは、関数と構造体が使用できるクロージャのタイプを指定する方法です。クロージャ本体が値を処理する方法に応じて、クロージャは 1 つ、2 つ、または 3 つの値を自動的かつ段階的に実装します。 Fn 特性。

  1. FnOnce 一度呼び出すことができるクロージャに適用されます。すべてのクロージャは呼び出すことができるため、すべてのクロージャは少なくともこの特性を実装します。キャプチャされた値をクロージャ本体の外に移動するクロージャは、実装のみを実装します。FnOnce 特性は一度しか呼び出すことができないためです。
  2. FnMut キャプチャされた値をクロージャ本体の外に移動しないが、キャプチャされた値を変更する可能性があるクロージャに適しています。このタイプのクロージャは複数回呼び出すことができます。
  3. Fn キャプチャされた値をクロージャ本体から移動したり、キャプチャされた値を変更したりしないクロージャに適用されます。もちろん、環境から値をキャプチャしないクロージャも含まれます。このようなクロージャは、環境を変更せずに複数回呼び出すことができます。これは、クロージャが同時に複数回呼び出されるシナリオでは重要です。

これを関数で使用する方法を示す例です。 FnOnce 一般的な制約としてクロージャが 1 回だけ呼び出されるようにします。

fn call_once<F, T>(f: F) -> T
where
    F: FnOnce() -> T, // 约束 F 为 FnOnce trait,意味着它接受一个空参数并返回 T 类型
{
    f() // 调用闭包并返回结果
}

fn main() {
    // 创建一个闭包,它捕获了 `value` 的所有权
    let value = 42;
    let consume = move || {
        let result = value; // 移动 `value`
        println!("The value is: {}", result);
        result // 返回结果
    };

    // 调用 `call_once` 函数,传入闭包
    let result = call_once(consume);
    println!("Result of the closure: {}", result);

    // 尝试再次使用 `consume` 将会导致编译错误,因为它已经消耗了 `value`
    // call_once(consume);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

演算結果

The value is: 42
Result of the closure: 42
  • 1
  • 2

この例では、ジェネリック関数を定義します。 call_onceのタイプを受け入れます。 F パラメーターf、で F 達成しなければならないFnOnce 特性。これはつまりf 空のパラメータを受け入れ、型を返すクロージャですT 結果として。

存在する main 関数内でクロージャを作成します。consumeをキャプチャします value 所有権の。それから、私たちは電話しますcall_once 関数、渡されるconsume 閉鎖。call_once 関数はクロージャを呼び出し、その結果を返します。なぜならconsume クロージャが消費されましたvalue、もう一度電話してみてください call_once 同じクロージャを渡すとコンパイル エラーが発生します。

ここに用途があります FnMut 特性の例。

fn apply_mut<F, T>(func: &mut F, num: i32) -> T
where
    F: FnMut(i32) -> T, // F 是一个可变闭包,接受一个 i32 类型的参数并返回类型为 T 的结果
{
    func(num) // 调用闭包并返回结果
}

fn main() {
    let mut count = 0;

    // 创建一个闭包,它接受一个 i32 类型的参数并将其加到 count 上
    let mut increment = |num: i32| -> i32 {
        count += num;
        count
    };

    // 使用 apply_mut 函数和 increment 闭包的引用
    let result: i32 = apply_mut(&mut increment, 5);
    println!("Result after applying increment: {}", result);

    // 再次使用 apply_mut 函数和 increment 闭包的引用
    let result: i32 = apply_mut(&mut increment, 10);
    println!("Result after applying increment again: {}", result);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

演算結果

Result after applying increment: 5
Result after applying increment again: 15
  • 1
  • 2

この例では、apply_mut この関数は、F 型の可変参照&mut F パラメータとして(つまり、increment クロージャーは移動せず、複数回使用できます)。i32 型パラメータnumwhere 句は指定しますF 実装である必要がありますFnMut クロージャー。i32 型パラメータを返し、次の型を返します。T 結果として。main 可変クロージャが関数内に作成されますincrement、キャプチャされた変数を変更します count、次に使用します apply_mut このクロージャを呼び出す関数。すべての電話apply_mut 使用しますincrement 閉鎖、そしてincrement クロージャが変更されますcount 価値。

Rustでは、Fn この特性は、クロージャがキャプチャした変数の所有権を取得せず、それらの変数を変更しないことを意味します。次の例は、accept を定義する方法を示しています。Fn 関数への引数としてクロージャを使用し、同時シナリオで使用します。

use std::thread;

// 定义一个函数,它接受一个实现了 Fn(i32) -> i32 的闭包,并调用它
fn call_once<F, T>(func: F) -> T
where
    F: Fn(i32) -> T, // 指定 F 是一个接受 i32 并返回 T 的 Fn 闭包
{
    let result = func(42); // 调用闭包,传入一个 i32 类型的值
    result // 返回闭包的执行结果
}

fn main() {
    // 定义一个简单的 Fn 闭包,它接受一个 i32 类型的参数并返回两倍的该值
    let double = |x: i32| -> i32 {
        x * 2
    };

    // 创建多个线程,每个线程都使用相同的闭包
    let handles: Vec<_> = (0..5).map(|i| {
        let func = double; // 闭包可以被复制,因为它是 Fn 类型的
        thread::spawn(move || {
            let result = call_once(func); // 调用 call_once 函数,并传入闭包
            println!("Thread {} result: {}", i, result); // 打印结果
        })
    }).collect();

    // 等待所有线程完成
    for handle in handles {
        handle.join().unwrap();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

すべての実行がこの順序になるわけではありません。どのスレッドが最初に実行されるかは、システムの現在のスケジュールによって異なります。

演算結果

Thread 1 result: 84
Thread 2 result: 84
Thread 0 result: 84
Thread 4 result: 84
Thread 3 result: 84
  • 1
  • 2
  • 3
  • 4
  • 5

この例では:

  1. を定義しました。 call_once 汎用パラメータを受け入れる関数F、そして F 満足しなければなりませんFn(i32) -> T 特性の限界。これはつまりF を受け入れるクロージャですi32 型パラメータを返し、次の型を返します。T 結果として。
  2. 存在する main 関数内で、単純なクロージャを定義します。doubleを受け入れます。 i32 型パラメータx そして戻るx * 2 結果として。
  3. を使用しております map 5 つのスレッドが作成され、各スレッドがコピーされますdouble 閉じて新しいスレッドで呼び出しますcall_once 関数、クロージャを引数として渡しますcall_once
  4. 各スレッドでは、call_once 関数が呼び出され、クロージャが実行され、結果が出力されます。
  5. 最後に、使用します join メソッドはすべてのスレッドが完了するまで待機します。

2. イテレータ

イテレータを使用すると、シーケンスの項目に対して特定の処理を実行できます。次に、要素シーケンスを処理するためのイテレータの使用と、ループとイテレータのパフォーマンスの比較を主に紹介します。 Rust では、イテレータは遅延的です。つまり、イテレータを使用するメソッドが呼び出されるまで操作は実行されません。

2.1 イテレータの特性と next メソッド

イテレータはすべて、と呼ばれる関数を実装します。 Iterator 標準ライブラリのトレイトで定義されています。この特性の定義は次のようになります。

pub trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
    // 此处省略了方法的默认实现
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

type Item そしてSelf::Item 、特性に関連付けられたタイプを定義します。しかし今のところ知っておくべきことは、このコードが実装方法を示しているということだけです。Iterator 特性には、Item タイプ、これItem タイプは次のように使用されますnext メソッドの戻り値の型。言い換えると、Item 型はイテレータによって返される要素の型になります。

next はいIterator メソッド実装者が定義する必要があるのはメソッドだけです。next イテレータ内で一度に 1 つの項目をカプセル化して返します。Some 、イテレータが終了すると戻りますNone

2.2 イテレータの使用方法

Iterator 特性には、標準ライブラリによってデフォルトで実装されるさまざまなメソッドのセットがあります。Iterator これらのメソッドはすべて、トレイトの標準ライブラリ API ドキュメントに記載されています。一部のメソッドはその定義を呼び出しますnext メソッドを実装する理由は次のとおりです。Iterator 特性を実装する必要がありますnext 方法の理由。

これらの通話 next メソッドのメソッドは、呼び出しによってイテレータが消費されるため、消費アダプタと呼ばれます。

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    // 使用into_iter()将Vec转换为消费迭代器
    let numbers_iter = numbers.iter();
    let mut sum: i32 = numbers_iter
        // 使用sum适配器计算迭代器中所有元素的总和
        .sum();

    // 打印总和
    println!("The sum is: {}", sum);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

再度使用する場合 numbers_iter エラーが報告されます。sum メソッドはイテレータの所有権を取得し、それを繰り返し呼び出します。next イテレータを反復処理して、イテレータを消費します。各項目を反復処理すると、各項目が合計に加算され、反復が完了すると合計が返されます。移行sum 使用は許可されなくなりましたnumbers_iter、電話するから sum イテレータの所有権を取得したとき。

2.3 他の反復子を生成するメソッド

Iterator イテレータ アダプタと呼ばれる、トレイトで定義された別のクラスのメソッドを使用すると、現在のイテレータを別の型のイテレータに変更できます。複数のイテレータ アダプタをチェーンで呼び出すことができます。ただし、すべての反復子は遅延型であるため、反復子のアダプター呼び出しの結果を取得するには、消費アダプター メソッドを呼び出す必要があります。

fn main() {
    let v1: Vec<i32> = vec![1, 2, 3];
    let v2: Vec<_> = v1.iter().map(|x| x * x).collect();
    assert_eq!(v2, vec![1, 4, 9]);
}
  • 1
  • 2
  • 3
  • 4
  • 5

collect メソッドはイテレータを使用し、結果をデータ構造に収集します。なぜならmap 通過する各要素に対して実行する操作を指定できるクロージャを取得します。

2.4 環境をキャプチャするクロージャの使用

多くの反復子アダプターはパラメーターとしてクロージャーを受け入れます。通常、反復子アダプターのパラメーターとして指定されたクロージャーは、その環境をキャプチャーするクロージャーになります。

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 使用into_iter()将Vec转换为消费迭代器
    let filtered_and_squared: Vec<i32> = numbers.into_iter()
        // 使用filter适配器过滤元素,接受一个闭包作为参数
        // 闭包捕获其环境,这里指numbers的元素
        .filter(|&x| x % 2 == 0) // 保留偶数
        // 使用map适配器对过滤后的元素进行变换,也接受一个闭包
        .map(|x| x * x) // 对每个元素进行平方
        // 使用collect适配器将结果收集到一个新的Vec中
        .collect();

    // 打印过滤和平方后的结果
    println!("The filtered and squared numbers are: {:?}", filtered_and_squared);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

演算結果

The filtered and squared numbers are: [4, 16]
  • 1

この例では、filter そしてmap どちらもイテレータ アダプタであり、クロージャをパラメータとして受け入れます。これらのクロージャは、その環境、つまりイテレータ内の要素をキャプチャします。存在するfilter アダプター、クロージャー内|&x| x % 2 == 0 要素が偶数かどうかを確認するために使用され、偶数の場合は要素が保持されます。存在するmap アダプター、クロージャー内|x| x * x 各要素を二乗するために使用されます。これらのアダプタはコンシューマであるため、元のイテレータを消費するため、再度使用することはできません。やっと、collect アダプターは、処理された要素を新しいファイルに収集します。Vec 真ん中。

2.5 ループと反復子のパフォーマンスの比較

イテレータは、高レベルの抽象化として、手書きの低レベル コードとほぼ同じパフォーマンスのコードにコンパイルされます。イテレータは Rust のゼロコスト抽象化の 1 つであり、この抽象化により実行時のオーバーヘッドが発生しないことを意味します。

fn main() {
    let numbers1 = (0..1000000).collect::<Vec<i64>>();
    let numbers2 = (0..1000000).collect::<Vec<i64>>();

    let mut sum1 = 0i64;
    let mut sum2 = 0i64;

    // 测量for循环的性能
    let start = std::time::Instant::now();
    for val in numbers1 {
        sum1 += val;
    }
    let loop_duration = start.elapsed();

    // 测量迭代器的性能
    let start = std::time::Instant::now();
    for val in numbers2.iter() {
        sum2 += val;
    }
    let iterator_duration = start.elapsed();

    println!("Iterator took: {:?}", iterator_duration);
    println!("For loop took: {:?}", loop_duration);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

演算結果

Iterator took: 12.796012ms
For loop took: 11.559512ms

Iterator took: 12.817732ms
For loop took: 11.687655ms

Iterator took: 12.75484ms
For loop took: 11.89468ms

Iterator took: 12.812022ms
For loop took: 11.785106ms

Iterator took: 12.78293ms
For loop took: 11.528941ms
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

この例から、イテレータはまだわずかに遅いことがわかります。

参考リンク

  1. Rust公式サイト:https://www.rust-lang.org/zh-CN
  2. Rustの公式ドキュメント:翻訳元:
  3. ラストプレイ:参考:
  4. 「Rustプログラミング言語」