De manera similar a trait bounds, se puede usar una sintaxis de trait implícita en argumentos de funciones y valores de retorno:
use std::fmt::Display;
fn get_x(name: impl Display) -> impl Display {
format!("Hello {name}")
}
fn main() {
let x = get_x("foo");
println!("{x}");
}
El significado de impl Trait es un poco diferente en las diferentes posiciones.
Para un parámetro, impl Trait es como un parámetro genérico anónimo con un trait vinculado.
Para un tipo de retorno, significa que el tipo de retorno es algún tipo concreto que implementa el trait, sin nombrar el tipo. Esto puede resultar útil cuando no desea exponer el tipo concreto en una API pública.
La inferencia es difícil en la posición de retorno. Una función que devuelve impl Foo elige el tipo concreto que devuelve, sin escribirlo en el código fuente. Una función que devuelve un tipo genérico como recopilar<B>() -> B puede devolver cualquier tipo que satisfaga B, y es posible que la persona que llama deba elegir uno, como con let x: Vec<_> = foo.collect() o foo.collect::<Vec<_>>().
Este ejemplo es fantástico porque utiliza impl Display dos veces. Es útil explicar que nada aquí exige que sea el mismo tipo de visualización implícito. Si usáramos un especificación de tipo T:, se impondría la restricción de que el tipo de entrada T y el de retorno T sean del mismo tipo. ¡No funcionaría para esta función en particular, ya que el tipo que esperamos como entrada probablemente no sea del mismo tipo que format!. Si quisiéramos hacer lo mismo mediante la especificación del tipo, necesitaríamos dos parámetros de tipo genéricos independientes.