tipo de estructura anónimo para el parámetro de función
En TypeScript, puedo hacer esto:
function foo(param: { a: string, b: number }) { }
Para declarar una función que toma un objeto, sin declarar explícitamente el tipo de parámetro como un tipo con nombre como este:
interface Parameter {
a: string;
b: number;
}
function foo(param: Parameter) {}
¿Hay alguna manera de que pueda hacer esto en Rust, o tengo que declarar explícitamente el tipo de parámetro como un tipo con nombre?
Respuestas
Rust tiene deconstrucción de patrones para parámetros de función en tuplas, matrices y estructuras como esta:
fn f((a, b): (u32, i32), [x, y, z]: [String; 3]) { }
struct A { a: u32, b: String }
fn g(A { a, b }: A) { }
Pero no tiene tal sintaxis para tipos / objetos sin nombre, ya que los objetos simplemente no existen en rust. Imagina que rust tiene una sintaxis para esto:
fn f(param: {a: String, b: String}) {} // Invalid code!
¿Cómo llamaría alguien a esa función? No hay forma de construir una instancia de este tipo. En javascript (/ mecanografiado) esto es posible, debido a la escritura dinámica, pero en rust tienes que conocer un tipo para poder construirlo.
Si está interesado en falsificar argumentos de palabras clave en funciones, esto podría ayudar: ¿Cómo mejorar los argumentos de función de estilo de palabra clave * falsos * en Rust?
Si desea dar un nombre a las tuplas además de tener un nombre para sus parámetros, existe la característica inestable que bindings_after_at
habilita esta sintaxis:
#![feature(bindings_after_at)]
fn f(my_tuple @ (a, b): (u32, u32)) {
println!("this: {:?}", my_tuple);
println!("is the same as: {:?}", (a, b));
}
// or this
fn g(arr @ [.., tail] : [u32; 5]) {
println!("this: {}", arr[4]);
println!("is the same as: {}", tail);
}
Puedes usar una tupla:
fn foo(param: (String, usize)) {
let a: String = param.0;
let b: usize = param.1;
}
En lugar de tener campos con nombre como estructuras, los valores de tupla se indexan. Desestructurar la tupla hace que sea un poco más fácil realizar un seguimiento de los valores:
fn foo((a, b): (String, usize)) {
// you can now access `a` and `b` here
}
No puede hacer tales cosas en Rust porque tiene un sistema de tipo nominal y su código es un ejemplo de sistemas de tipo estructural. Puedes leer sobre ellos en wikipedia:https://en.wikipedia.org/wiki/Nominal_type_system https://en.wikipedia.org/wiki/Structural_type_system
En el sistema de tipo estructural, el tipo es solo un conjunto de sus campos y su nombre y definición no importan. En contraste, el sistema de tipos nominales trata 2 tipos con declaración diferente pero el mismo conjunto de campos como diferentes (es decir, ese nombre del tipo es más importante que los contenidos).
Rust elegido nominal porque es más estricto y permite aplicar algunas propiedades del programa a nivel de tipo. Considere este ejemplo:
struct Employee(String);
struct Customer(String);
fn handle_order(employee: Employee, customer: Customer){}
Si el programador cometió un error y lo llama como handle_order(customer, employee)
, es un error que no se notará en el lenguaje con tipado estructural, pero provocaría un error de compilación en el tipado nominal.
Además, puede haber una situación en la que el programador necesite cambiar la definición del tipo, por ejemplo, agregar un campo al archivo Employee
. En tal caso, uno puede estar seguro de que la refactorización se realiza cuando todos los usos de Employee se arreglan. En un programa con tipificación estructural, uno no puede estar seguro porque puede haber un código que envíe al Cliente en lugar de y, por lo tanto, la refactorización de programas de tipificación estructural es un poco más difícil.
Otro ejemplo famoso de mecanografía nominal en Rust son las vidas . Las variables con tipos con el mismo tipo definido y diferentes duraciones en realidad tienen diferentes tipos nominales. Y toda la seguridad de Rust se basa en esto.
TypeScript usa tipado estructural porque es más fácil mapearlo en JavaScript.