Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

impl Trait

impl Trait можна використовувати в двох місцях:

  1. як тип аргументу
  2. як тип повернення

Як тип аргументу

Якщо ваша функція є узагальненою за трейтом, але вам не важливий конкретний тип, ви можете спростити оголошення функції, використовуючи impl Trait як тип аргументу.

Наприклад, розгляньте такий код:

fn parse_csv_document<R: std::io::BufRead>(src: R) -> std::io::Result<Vec<Vec<String>>> {
    src.lines()
        .map(|line| {
            // For each line in the source
            line.map(|line| {
                // If the line was read successfully, process it, if not, return the error
                line.split(',') // Split the line separated by commas
                    .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace
                    .collect() // Collect all strings in a row into a Vec<String>
            })
        })
        .collect() // Collect all lines into a Vec<Vec<String>>
}

parse_csv_document є узагальненою, що дає змогу їй приймати будь-який тип, який реалізує BufRead, наприклад BufReader<File> або [u8], але неважливо, який саме тип R, і R використовується лише для оголошення типу src, тож функцію також можна записати так:

fn parse_csv_document(src: impl std::io::BufRead) -> std::io::Result<Vec<Vec<String>>> {
    src.lines()
        .map(|line| {
            // For each line in the source
            line.map(|line| {
                // If the line was read successfully, process it, if not, return the error
                line.split(',') // Split the line separated by commas
                    .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace
                    .collect() // Collect all strings in a row into a Vec<String>
            })
        })
        .collect() // Collect all lines into a Vec<Vec<String>>
}

Зауважте, що використання impl Trait як типу аргументу означає, що ви не можете явно вказати, яку форму функції ви використовуєте, тобто parse_csv_document::<std::io::Empty>(std::io::empty()) не працюватиме з другим прикладом.

Як тип повернення

Якщо ваша функція повертає тип, який реалізує MyTrait, ви можете записати її тип повернення як -> impl MyTrait. Це може допомогти значно спростити ваші сигнатури типів!

use std::iter;
use std::vec::IntoIter;

// This function combines two `Vec<i32>` and returns an iterator over it.
// Look how complicated its return type is!
fn combine_vecs_explicit_return_type(
    v: Vec<i32>,
    u: Vec<i32>,
) -> iter::Cycle<iter::Chain<IntoIter<i32>, IntoIter<i32>>> {
    v.into_iter().chain(u.into_iter()).cycle()
}

// This is the exact same function, but its return type uses `impl Trait`.
// Look how much simpler it is!
fn combine_vecs(
    v: Vec<i32>,
    u: Vec<i32>,
) -> impl Iterator<Item=i32> {
    v.into_iter().chain(u.into_iter()).cycle()
}

fn main() {
    let v1 = vec![1, 2, 3];
    let v2 = vec![4, 5];
    let mut v3 = combine_vecs(v1, v2);
    assert_eq!(Some(1), v3.next());
    assert_eq!(Some(2), v3.next());
    assert_eq!(Some(3), v3.next());
    assert_eq!(Some(4), v3.next());
    assert_eq!(Some(5), v3.next());
    println!("all done");
}

Що важливіше, деякі типи Rust неможливо записати явно. Наприклад, кожне замикання має свій власний безіменний конкретний тип. До синтаксису impl Trait вам доводилося виділяти пам’ять у купі, щоб повернути замикання. Але тепер ви можете зробити все статично, ось так:

// Returns a function that adds `y` to its input
fn make_adder_function(y: i32) -> impl Fn(i32) -> i32 {
    let closure = move |x: i32| { x + y };
    closure
}

fn main() {
    let plus_one = make_adder_function(1);
    assert_eq!(plus_one(2), 3);
}

Ви також можете використовувати impl Trait, щоб повертати ітератор, який використовує замикання map або filter! Це робить використання map і filter простішим. Оскільки типи замикань не мають назв, ви не можете записати явний тип повернення, якщо ваша функція повертає ітератори із замиканнями. Але з impl Trait ви можете легко зробити це:

fn double_positives<'a>(numbers: &'a Vec<i32>) -> impl Iterator<Item = i32> + 'a {
    numbers
        .iter()
        .filter(|x| x > &&0)
        .map(|x| x * 2)
}

fn main() {
    let singles = vec![-3, -2, 2, 3];
    let doubles = double_positives(&singles);
    assert_eq!(doubles.collect::<Vec<i32>>(), vec![4, 6]);
}