기술나눔

Rust 프로그래밍 언어 학습 - 기능적 언어 기능: 반복자와 클로저

2024-07-12

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

클로저는 변수에 저장되거나 다른 함수에 인수로 전달될 수 있는 익명 함수입니다. 한 곳에서 클로저를 생성한 다음 다른 컨텍스트에서 클로저 작업을 수행할 수 있습니다. 함수와 달리 클로저는 정의된 범위 내에서 값을 캡처할 수 있습니다.

반복자는 시퀀스의 각 항목을 순회하고 시퀀스가 ​​끝나는 시점을 결정하는 논리를 담당합니다. 반복자를 사용할 때 이 논리를 다시 구현할 필요는 없습니다.

1. 폐쇄

클로저(Closure)는 변수에 저장할 수 있는 함수와 유사한 구조입니다.

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

이 클로저 정의는 유형 주석을 추가하지 않으므로 어떤 유형으로도 이 클로저를 호출할 수 있습니다. 그러나 처음에는 i32를 사용하고 두 번째에는 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 특성에서 캡처된 값 이동

클로저가 정의된 환경의 값에 대한 참조 또는 소유권을 캡처하면(따라서 클로저로 이동되는 항목에 영향을 미침) 클로저 본문 내의 코드는 나중에 클로저가 참조를 정의합니다. 또는 값이 조작되는 방식(클로저에서 제거되는 항목에 영향을 미침) 클로저 본문은 캡처된 값을 클로저 밖으로 이동하거나, 캡처된 값을 수정하거나, 값을 이동하거나 수정하지 않거나, 애초에 환경에서 값을 캡처하지 않는 등의 작업을 수행할 수 있습니다.

클로저가 환경의 값을 캡처하고 처리하는 방식은 클로저가 구현하는 특성에 영향을 미칩니다. 특성은 함수와 구조가 사용할 수 있는 클로저 유형을 지정하는 방법입니다.클로저 본문이 값을 처리하는 방법에 따라 클로저는 자동으로 점진적으로 하나, 둘 또는 셋을 구현합니다.Fn 특성.

  1. FnOnce 한 번 호출할 수 있는 클로저에 적용됩니다. 모든 클로저가 호출될 수 있기 때문에 최소한 이 특성을 구현합니다.캡처된 값을 클로저 본문 밖으로 이동하는 클로저는 구현만 수행합니다.FnOnce 특성은 한 번만 호출할 수 있기 때문입니다.
  2. FnMut 캡처된 값을 클로저 본체 밖으로 이동하지 않는 클로저에 적합하지만 캡처된 값을 수정할 수 있습니다. 이러한 유형의 클로저는 여러 번 호출될 수 있습니다.
  3. Fn 캡처된 값을 클로저 본체 밖으로 이동하거나 캡처된 값을 수정하지 않는 클로저에 적용되며 물론 환경에서 값을 캡처하지 않는 클로저도 포함됩니다. 이러한 클로저는 환경을 변경하지 않고도 여러 번 호출할 수 있으며, 이는 클로저가 동시에 여러 번 호출되는 시나리오에서 중요합니다.

다음은 함수에서 이를 사용하는 방법을 보여주는 예입니다. FnOnce 일반 제약 조건으로 클로저가 한 번만 호출되는지 확인하세요.

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 값.

러스트에서는Fn 이 특성은 클로저가 캡처한 변수의 소유권을 가지지 않으며 해당 변수를 변경하지 않는다는 것을 의미합니다.다음 예에서는 수락을 정의하는 방법을 보여줍니다.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. 반복자

반복자를 사용하면 시퀀스 항목에 대해 특정 처리를 수행할 수 있습니다. 다음으로 반복자를 사용하여 요소 시퀀스를 처리하고 루프 VS 반복자의 성능 비교를 주로 소개합니다. 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 유형은 반복자가 반환한 요소의 유형이 됩니다.

nextIterator 유일한 메소드 구현자는 정의해야 합니다.next 반복자에서 한 번에 하나의 항목을 캡슐화하여 반환합니다.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 루프 VS 반복자 성능 비교

상위 수준 추상화인 반복자는 직접 작성한 하위 수준 코드와 거의 동일한 성능을 갖는 코드로 컴파일됩니다. 반복자는 Rust의 비용이 들지 않는 추상화 중 하나입니다. 즉, 추상화로 인해 런타임 오버헤드가 발생하지 않는다는 의미입니다.

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. 러스트 공식 웹사이트:한국어: https://www.rust-lang.org/zh-CN
  2. Rust 공식 문서:한국어: https://doc.rust-lang.org/
  3. 러스트 플레이:https://play.rust-lang.org/
  4. "러스트 프로그래밍 언어"