anonimowy typ struktury dla parametru funkcji

Dec 23 2020

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

3 Natrix Dec 24 2020 at 05:03

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_atfunkcja, 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);
}
2 IbraheemAhmed Dec 24 2020 at 03:51

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
}
2 AngelicosPhosphoros Dec 24 2020 at 19:11

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.