anonymer Strukturtyp für Funktionsparameter
In Typescript kann ich dies tun:
function foo(param: { a: string, b: number }) { }
So deklarieren Sie eine Funktion, die ein Objekt aufnimmt, ohne den Parametertyp explizit als benannten Typ wie folgt zu deklarieren:
interface Parameter {
a: string;
b: number;
}
function foo(param: Parameter) {}
Gibt es eine Möglichkeit, dies in Rust zu tun, oder muss ich den Parametertyp explizit als benannten Typ deklarieren?
Antworten
Rust hat eine Musterdekonstruktion für Funktionsparameter auf Tupeln, Arrays und Strukturen wie folgt:
fn f((a, b): (u32, i32), [x, y, z]: [String; 3]) { }
struct A { a: u32, b: String }
fn g(A { a, b }: A) { }
Es gibt jedoch keine solche Syntax für unbenannte Typen / Objekte, da Objekte in Rost einfach nicht existieren. Stellen Sie sich vor, Rost hätte eine Syntax dafür:
fn f(param: {a: String, b: String}) {} // Invalid code!
Wie würde jemand diese Funktion nennen? Es gibt keine Möglichkeit, eine Instanz dieses Typs zu erstellen. In Javascript (/ typescript) ist dies aufgrund der dynamischen Typisierung möglich, aber in Rost muss man einen Typ kennen, um ihn erstellen zu können.
Wenn Sie daran interessiert sind, Schlüsselwortargumente in Funktionen zu fälschen, kann dies helfen: Wie können Sie Funktionsargumente im Schlüsselwortstil in Rust am besten * fälschen *?
Wenn Sie den Tupeln einen Namen geben und ihren Parametern einen Namen geben möchten, gibt es die instabile bindings_after_at
Funktion, die diese Syntax aktiviert:
#![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);
}
Sie können ein Tupel verwenden:
fn foo(param: (String, usize)) {
let a: String = param.0;
let b: usize = param.1;
}
Anstatt benannte Felder wie Strukturen zu haben, werden Tupelwerte indiziert. Durch die Zerstörung des Tupels ist es etwas einfacher, die Werte im Auge zu behalten:
fn foo((a, b): (String, usize)) {
// you can now access `a` and `b` here
}
Sie können solche Dinge in Rust nicht tun, da es ein nominales Typsystem hat und Ihr Code ein Beispiel für strukturelle Typsysteme ist. Sie können darüber in Wikipedia lesen:https://en.wikipedia.org/wiki/Nominal_type_system https://en.wikipedia.org/wiki/Structural_type_system
Im strukturellen Typsystem besteht der Typ nur aus seinen Feldern, und Name und Definition spielen keine Rolle. Im Gegensatz dazu behandelt das nominelle Typsystem zwei Typen mit unterschiedlicher Deklaration, aber demselben Satz von Feldern als unterschiedlich (dh dieser Name des Typs ist wichtiger als der Inhalt).
Rust hat nominal gewählt, weil es strenger ist und es ermöglicht, einige Eigenschaften des Programms auf Typebene durchzusetzen. Betrachten Sie dieses Beispiel:
struct Employee(String);
struct Customer(String);
fn handle_order(employee: Employee, customer: Customer){}
Wenn der Programmierer einen Fehler gemacht hat und ihn so nennt handle_order(customer, employee)
, ist dies ein Fehler, der in der Sprache mit struktureller Typisierung nicht bemerkt wird, aber einen Kompilierungsfehler bei der nominalen Typisierung auslöst.
Es kann auch vorkommen, dass Programmierer die Definition des Typs ändern müssen, z. B. ein Feld zum hinzufügen Employee
. In einem solchen Fall kann man sicher sein, dass das Refactoring durchgeführt wird, wenn alle Verwendungen des Mitarbeiters behoben sind. In Programmen mit struktureller Typisierung kann man nicht sicher sein, da es einen Code geben kann, der den Kunden anstelle von strukturell typisierten Programmen sendet, und daher ist das Refactoring von strukturell typisierten Programmen etwas schwieriger.
Ein weiteres berühmtes Beispiel für die nominelle Eingabe in Rust sind die Lebensdauern . Variablen mit Typen mit demselben definierten Typ und unterschiedlichen Lebensdauern haben tatsächlich unterschiedliche Nenntypen. Und alle Sicherheit von Rusts basiert darauf.
Typescript verwendet strukturelle Typisierung, da es einfacher ist, es in JavaScript abzubilden.