tipe struct anonim untuk parameter fungsi
Di Ketikan, saya bisa melakukan ini:
function foo(param: { a: string, b: number }) { }
Untuk mendeklarasikan fungsi yang mengambil objek, tanpa secara eksplisit mendeklarasikan tipe parameter sebagai tipe bernama seperti ini:
interface Parameter {
a: string;
b: number;
}
function foo(param: Parameter) {}
Apakah ada cara saya dapat melakukan ini di Rust, atau apakah saya harus secara eksplisit menyatakan tipe parameter sebagai tipe bernama?
Jawaban
Rust memiliki dekonstruksi pola untuk parameter fungsi pada tupel, array, dan struct seperti ini:
fn f((a, b): (u32, i32), [x, y, z]: [String; 3]) { }
struct A { a: u32, b: String }
fn g(A { a, b }: A) { }
Tetapi tidak memiliki sintaks seperti itu untuk tipe / objek yang tidak disebutkan namanya, karena objek tidak ada dalam karat. Bayangkan karat memiliki sintaks untuk ini:
fn f(param: {a: String, b: String}) {} // Invalid code!
Bagaimana seseorang menyebut fungsi itu? Tidak ada cara untuk membuat instance jenis ini. Dalam javascript (/ typescript) hal ini dimungkinkan, karena pengetikan dinamis, tetapi di Rust Anda harus mengetahui jenis untuk dapat membangunnya.
Jika Anda tertarik untuk memalsukan argumen kata kunci dalam fungsi, ini mungkin membantu: Bagaimana cara terbaik argumen fungsi gaya kata kunci * palsu * di Rust?
Jika Anda ingin memberi nama pada tupel serta memiliki nama parameternya, ada fitur bindings_after_at
-fitur tidak stabil yang memungkinkan sintaks ini:
#![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);
}
Anda dapat menggunakan tupel:
fn foo(param: (String, usize)) {
let a: String = param.0;
let b: usize = param.1;
}
Daripada memiliki bidang bernama seperti struct, nilai tuple diindeks. Merusak tupel membuatnya sedikit lebih mudah untuk melacak nilainya:
fn foo((a, b): (String, usize)) {
// you can now access `a` and `b` here
}
Anda tidak dapat melakukan hal-hal seperti itu di Rust karena ia memiliki sistem tipe nominal dan kode Anda adalah contoh sistem tipe struktural. Anda dapat membacanya di wikipedia:https://en.wikipedia.org/wiki/Nominal_type_system https://en.wikipedia.org/wiki/Structural_type_system
Dalam sistem tipe struktural, tipe hanyalah kumpulan bidangnya dan nama serta definisinya tidak masalah. Sebaliknya, sistem tipe nominal memperlakukan 2 tipe dengan deklarasi berbeda tetapi kumpulan field yang sama sebagai berbeda (artinya, nama tipe lebih penting daripada isinya).
Rust dipilih nominal karena lebih ketat dan memungkinkan untuk menegakkan beberapa properti program pada level tipe. Pertimbangkan contoh ini:
struct Employee(String);
struct Customer(String);
fn handle_order(employee: Employee, customer: Customer){}
Jika programmer melakukan kesalahan dan menyebutnya seperti handle_order(customer, employee)
, itu adalah kesalahan yang tidak akan diperhatikan dalam bahasa dengan pengetikan struktural tetapi akan memicu kesalahan kompilasi pada pengetikan nominal.
Juga, ada situasi ketika programmer perlu mengubah definisi tipe, misalnya, menambahkan field ke Employee
. Dalam kasus seperti itu, dapat dipastikan bahwa refactoring dilakukan ketika semua penggunaan Employee diperbaiki. Dalam program dengan pengetikan struktural, seseorang tidak dapat memastikannya karena mungkin ada kode yang mengirim Pelanggan alih-alih dan dengan demikian pemfaktoran ulang program jenis struktural sedikit lebih sulit.
Contoh terkenal lainnya dari pengetikan nominal di Rust adalah masa pakai . Variabel dengan tipe dengan tipe yang ditentukan sama dan masa pakai yang berbeda sebenarnya memiliki tipe nominal yang berbeda. Dan semua keamanan Rusts didasarkan pada ini.
Ketikan terstruktur menggunakan ketikan struktural karena lebih mudah untuk memetakannya ke dalam JavaScript.