анонимный тип структуры для параметра функции

Dec 23 2020

В Typescript я могу сделать это:

function foo(param: { a: string, b: number }) { }

Чтобы объявить функцию, которая принимает объект, без явного объявления типа параметра как именованного типа, например:

interface Parameter {
    a: string;
    b: number;
}

function foo(param: Parameter) {}

Есть ли способ сделать это в Rust, или мне нужно явно объявить тип параметра как именованный тип?

Ответы

3 Natrix Dec 24 2020 at 05:03

В Rust есть шаблонная деконструкция для параметров функций в кортежах, массивах и структурах, например:

fn f((a, b): (u32, i32), [x, y, z]: [String; 3]) { }
struct A { a: u32, b: String }
fn g(A { a, b }: A) { }

Но у него нет такого синтаксиса для безымянных типов / объектов, поскольку объекты просто не существуют в ржавчине. Представьте, что у ржавчины есть синтаксис для этого:

fn f(param: {a: String, b: String}) {} // Invalid code!

Как можно назвать эту функцию? Невозможно создать экземпляр этого типа. В javascript (/ typescript) это возможно из-за динамической типизации, но в ржавчине вы должны знать тип, чтобы иметь возможность его построить.

Если вас интересует подделка аргументов ключевого слова в функциях, это может помочь: Как лучше всего подделать аргументы функции стиля ключевого слова * fake * в Rust?

Если вы хотите дать кортежам имя, а также имя для их параметров, существует нестабильная bindings_after_atфункция, включающая этот синтаксис:

#![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

Вы можете использовать кортеж:

fn foo(param: (String, usize)) {
    let a: String = param.0;
    let b: usize = param.1;
}

Вместо именованных полей, таких как структуры, индексируются значения кортежей. Разрушение кортежа упрощает отслеживание значений:

fn foo((a, b): (String, usize)) {
    // you can now access `a` and `b` here
}
2 AngelicosPhosphoros Dec 24 2020 at 19:11

Вы не можете делать такие вещи в Rust, потому что он имеет систему номинальных типов, а ваш код является примером систем структурных типов. О них можно прочитать в Википедии:https://en.wikipedia.org/wiki/Nominal_type_system https://en.wikipedia.org/wiki/Structural_type_system

В системе структурных типов тип - это просто набор его полей, и его имя и определение не имеют значения. Напротив, система номинальных типов рассматривает 2 типа с разным объявлением, но с одним и тем же набором полей как разные (что означает, что имя типа более важно, чем содержимое).

Rust выбрал номинальный, потому что он более строгий и позволяет применять некоторые свойства программы на уровне типа. Рассмотрим этот пример:

struct Employee(String);
struct Customer(String);

fn handle_order(employee: Employee, customer: Customer){}

Если программист допустил ошибку и назвал это так handle_order(customer, employee), это ошибка, которая не будет замечена в языке со структурной типизацией, но вызовет ошибку компиляции при номинальной типизации.

Также может возникнуть ситуация, когда программисту нужно изменить определение типа, например, добавить поле в Employee. В таком случае можно быть уверенным, что рефакторинг выполнен, когда все использования Employee исправлены. В программе со структурной типизацией нельзя быть уверенным, потому что может быть код, который отправляет Customer вместо, и поэтому рефакторинг программ со структурной типизацией немного сложнее.

Другой известный пример номинальной типизации в Rust - время жизни . Переменные с типами с одним и тем же определенным типом и разным временем жизни фактически имеют разные номинальные типы. И вся безопасность Rusts основана на этом.

В Typescript используется структурная типизация, потому что ее легче отобразить в JavaScript.