anonimowy typ struktury dla parametru funkcji
Na maszynie mogę to zrobić:
function foo(param: { a: string, b: number }) { }
Aby zadeklarować funkcję, która przyjmuje obiekt, bez jawnego deklarowania typu parametru jako nazwanego typu, jak poniżej:
interface Parameter {
a: string;
b: number;
}
function foo(param: Parameter) {}
Czy istnieje sposób, w jaki mogę to zrobić w Rust, czy też muszę jawnie zadeklarować typ parametru jako nazwany typ?
Odpowiedzi
Rust ma dekonstrukcję wzorców dla parametrów funkcji krotek, tablic i struktur, takich jak ta:
fn f((a, b): (u32, i32), [x, y, z]: [String; 3]) { }
struct A { a: u32, b: String }
fn g(A { a, b }: A) { }
Ale nie ma takiej składni dla nienazwanych typów / obiektów, ponieważ obiekty po prostu nie istnieją w rdzeniu. Wyobraź sobie, że rust ma składnię odpowiadającą temu:
fn f(param: {a: String, b: String}) {} // Invalid code!
Jak ktoś nazwałby tę funkcję? Nie ma możliwości zbudowania instancji tego typu. W javascript (/ typescript) jest to możliwe dzięki dynamicznemu pisaniu, ale w rdzeniu musisz znać typ, aby móc go skonstruować.
Jeśli jesteś zainteresowany fałszowaniem argumentów słów kluczowych w funkcjach, może to pomóc: Jak najlepiej * fałszywe * argumenty funkcji w stylu słów kluczowych w Rust?
Jeśli chcesz nadać krotkom nazwę, a także nadać im nazwę ich parametrom, istnieje niestabilna bindings_after_at
funkcja, która umożliwia następującą składnię:
#![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);
}
Możesz użyć krotki:
fn foo(param: (String, usize)) {
let a: String = param.0;
let b: usize = param.1;
}
Zamiast mieć nazwane pola, takie jak struktury, indeksowane są wartości krotek. Zniszczenie krotki ułatwia śledzenie wartości:
fn foo((a, b): (String, usize)) {
// you can now access `a` and `b` here
}
Nie możesz robić takich rzeczy w Rust, ponieważ ma system typów nominalnych, a Twój kod jest przykładem systemów typów strukturalnych. Możesz o nich poczytać w Wikipedii:https://en.wikipedia.org/wiki/Nominal_type_system https://en.wikipedia.org/wiki/Structural_type_system
W systemie typów strukturalnych typ to po prostu zestaw pól, a jego nazwa i definicja nie mają znaczenia. W przeciwieństwie do tego system typów nominalnych traktuje 2 typy z różnymi deklaracjami, ale tym samym zestawem pól jako różne (co oznacza, że nazwa typu jest ważniejsza niż zawartość).
Rust wybrał nominalną, ponieważ jest ostrzejszy i pozwala wymusić pewne właściwości programu na poziomie typu. Rozważmy ten przykład:
struct Employee(String);
struct Customer(String);
fn handle_order(employee: Employee, customer: Customer){}
Jeśli programista popełnił błąd i nazwał to tak handle_order(customer, employee)
, jest to błąd, który nie zostałby zauważony w języku z typowaniem strukturalnym, ale spowodowałby błąd kompilacji przy typowaniu nominalnym.
Może również zaistnieć sytuacja, w której programista będzie musiał zmienić definicję typu, na przykład dodać pole do Employee
. W takim przypadku można być pewnym, że refaktoryzacja została przeprowadzona, gdy wszystkie zastosowania pracownika zostały naprawione. W programie z typowaniem strukturalnym nie można być pewnym, ponieważ może istnieć kod, który wysyła Klienta zamiast tego, a zatem refaktoryzacja programów z typem strukturalnym jest trochę trudniejsza.
Innym znanym przykładem nominalnego pisania w Rust to okresy życia . Zmienne z typami o tym samym zdefiniowanym typie i różnych okresach istnienia w rzeczywistości mają różne typy nominalne. I na tym opiera się całe bezpieczeństwo firmy Rusts.
Typescript używa strukturalnego pisania, ponieważ łatwiej jest odwzorować go na JavaScript.