Explorando la funcionalidad de `impl Trait` en Rust

RustRustBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, exploraremos el uso de impl Trait en Rust, que se puede utilizar como un tipo de argumento o un tipo de retorno, lo que simplifica las declaraciones de funciones y permite un código más flexible y conciso.

Nota: Si el laboratorio no especifica un nombre de archivo, puede usar cualquier nombre de archivo que desee. Por ejemplo, puede usar main.rs, compilar y ejecutarlo con rustc main.rs &&./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/AdvancedTopicsGroup(["Advanced Topics"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/BasicConceptsGroup -.-> rust/mutable_variables("Mutable Variables") rust/DataTypesGroup -.-> rust/integer_types("Integer Types") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") rust/AdvancedTopicsGroup -.-> rust/traits("Traits") rust/AdvancedTopicsGroup -.-> rust/operator_overloading("Traits for Operator Overloading") subgraph Lab Skills rust/variable_declarations -.-> lab-99219{{"Explorando la funcionalidad de `impl Trait` en Rust"}} rust/mutable_variables -.-> lab-99219{{"Explorando la funcionalidad de `impl Trait` en Rust"}} rust/integer_types -.-> lab-99219{{"Explorando la funcionalidad de `impl Trait` en Rust"}} rust/function_syntax -.-> lab-99219{{"Explorando la funcionalidad de `impl Trait` en Rust"}} rust/expressions_statements -.-> lab-99219{{"Explorando la funcionalidad de `impl Trait` en Rust"}} rust/method_syntax -.-> lab-99219{{"Explorando la funcionalidad de `impl Trait` en Rust"}} rust/traits -.-> lab-99219{{"Explorando la funcionalidad de `impl Trait` en Rust"}} rust/operator_overloading -.-> lab-99219{{"Explorando la funcionalidad de `impl Trait` en Rust"}} end

impl Trait

impl Trait se puede utilizar en dos ubicaciones:

  1. como un tipo de argumento
  2. como un tipo de retorno

Como un tipo de argumento

Si su función es genérica sobre un trato pero no le importa el tipo específico, puede simplificar la declaración de la función utilizando impl Trait como el tipo del argumento.

Por ejemplo, considere el siguiente código:

fn parse_csv_document<R: std::io::BufRead>(src: R) -> std::io::Result<Vec<Vec<String>>> {
    src.lines()
     .map(|line| {
            // Para cada línea en la fuente
            line.map(|line| {
                // Si la línea se leyó correctamente, procesarla, si no, devolver el error
                line.split(',') // Dividir la línea separada por comas
                 .map(|entry| String::from(entry.trim())) // Quitar los espacios en blanco al principio y al final
                 .collect() // Recopilar todas las cadenas en una fila en un Vec<String>
            })
        })
     .collect() // Recopilar todas las líneas en un Vec<Vec<String>>
}

parse_csv_document es genérica, lo que le permite tomar cualquier tipo que implemente BufRead, como BufReader<File> o [u8], pero no es importante de qué tipo es R, y R solo se utiliza para declarar el tipo de src, por lo que la función también se puede escribir como:

fn parse_csv_document(src: impl std::io::BufRead) -> std::io::Result<Vec<Vec<String>>> {
    src.lines()
     .map(|line| {
            // Para cada línea en la fuente
            line.map(|line| {
                // Si la línea se leyó correctamente, procesarla, si no, devolver el error
                line.split(',') // Dividir la línea separada por comas
                 .map(|entry| String::from(entry.trim())) // Quitar los espacios en blanco al principio y al final
                 .collect() // Recopilar todas las cadenas en una fila en un Vec<String>
            })
        })
     .collect() // Recopilar todas las líneas en un Vec<Vec<String>>
}

Tenga en cuenta que utilizar impl Trait como un tipo de argumento significa que no puede declarar explícitamente qué forma de la función utiliza, es decir, parse_csv_document::<std::io::Empty>(std::io::empty()) no funcionará con el segundo ejemplo.

Como un tipo de retorno

Si su función devuelve un tipo que implementa MyTrait, puede escribir su tipo de retorno como -> impl MyTrait. Esto puede ayudar a simplificar sus firmas de tipo en gran medida.

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

// Esta función combina dos `Vec<i32>` y devuelve un iterador sobre él.
// Mira lo complicado que es su tipo de retorno!
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()
}

// Esta es exactamente la misma función, pero su tipo de retorno utiliza `impl Trait`.
// Mira lo mucho más simple que es!
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!("todo hecho");
}

Más importante aún, algunos tipos de Rust no se pueden escribir. Por ejemplo, cada clausura tiene su propio tipo concreto sin nombre. Antes de la sintaxis impl Trait, tenía que asignar memoria en el montón para devolver una clausura. Pero ahora puede hacerlo todo de forma estática, así:

// Devuelve una función que suma `y` a su entrada
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);
}

También puede utilizar impl Trait para devolver un iterador que utiliza clausuras map o filter! Esto hace que sea más fácil utilizar map y filter. Debido a que los tipos de clausura no tienen nombres, no puede escribir un tipo de retorno explícito si su función devuelve iteradores con clausuras. Pero con impl Trait puede hacer esto fácilmente:

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]);
}

Resumen

¡Felicitaciones! Has completado el laboratorio de Impl Trait. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.