Apakah mungkin untuk membuat fungsi dengan argumen default?
fn add(a: int = 1, b: int = 2) { a + b }
function
parameters
arguments
rust
Jeroen
sumber
sumber
Option
dan secara eksplisit meneruskanNone
.Jawaban:
Tidak, saat ini tidak. Saya pikir kemungkinan itu pada akhirnya akan diterapkan, tetapi tidak ada pekerjaan aktif di ruang ini saat ini.
Teknik khas yang digunakan di sini adalah menggunakan fungsi atau metode dengan nama dan tanda tangan yang berbeda.
sumber
Karena argumen default tidak didukung, Anda bisa mendapatkan perilaku serupa menggunakan
Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 { a.unwrap_or(1) + b.unwrap_or(2) }
Ini menyelesaikan tujuan untuk memiliki nilai default dan fungsi yang dikodekan hanya sekali (bukan di setiap panggilan), tetapi tentu saja lebih banyak untuk diketik. Panggilan fungsi akan terlihat seperti
add(None, None)
, yang mungkin Anda suka atau tidak suka tergantung pada perspektif Anda.Jika Anda tidak melihat ketikan apa pun dalam daftar argumen karena pembuat kode berpotensi lupa membuat pilihan, maka keuntungan besar di sini adalah secara eksplisit; pemanggil secara eksplisit mengatakan mereka ingin menggunakan nilai default Anda, dan akan mendapatkan kesalahan kompilasi jika mereka tidak memasukkan apa pun. Anggap saja sebagai mengetik
add(DefaultValue, DefaultValue)
.Anda juga bisa menggunakan makro:
fn add(a: i32, b: i32) -> i32 { a + b } macro_rules! add { ($a: expr) => { add($a, 2) }; () => { add(1, 2) }; }
assert_eq!(add!(), 3); assert_eq!(add!(4), 6);
Perbedaan besar antara kedua solusi tersebut adalah bahwa dengan argumen "Option" -al itu benar-benar valid untuk ditulis
add(None, Some(4))
, tetapi dengan pola makro yang cocok Anda tidak bisa (ini mirip dengan aturan argumen default Python).Anda juga bisa menggunakan struct "argumen" dan
From
/Into
sifat:pub struct FooArgs { a: f64, b: i32, } impl Default for FooArgs { fn default() -> Self { FooArgs { a: 1.0, b: 1 } } } impl From<()> for FooArgs { fn from(_: ()) -> Self { Self::default() } } impl From<f64> for FooArgs { fn from(a: f64) -> Self { Self { a: a, ..Self::default() } } } impl From<i32> for FooArgs { fn from(b: i32) -> Self { Self { b: b, ..Self::default() } } } impl From<(f64, i32)> for FooArgs { fn from((a, b): (f64, i32)) -> Self { Self { a: a, b: b } } } pub fn foo<A>(arg_like: A) -> f64 where A: Into<FooArgs>, { let args = arg_like.into(); args.a * (args.b as f64) } fn main() { println!("{}", foo(())); println!("{}", foo(5.0)); println!("{}", foo(-3)); println!("{}", foo((2.0, 6))); }
Pilihan ini jelas lebih banyak kode, tetapi tidak seperti desain makro, ia menggunakan sistem tipe yang berarti kesalahan kompilator akan lebih membantu pengguna perpustakaan / API Anda. Ini juga memungkinkan pengguna untuk membuat
From
implementasi mereka sendiri jika itu berguna bagi mereka.sumber
Tidak, Rust tidak mendukung argumen fungsi default. Anda harus menentukan metode berbeda dengan nama berbeda. Tidak ada fungsi yang berlebihan juga, karena Rust menggunakan nama fungsi untuk mendapatkan tipe (kelebihan beban membutuhkan yang sebaliknya).
Dalam kasus inisialisasi struct Anda dapat menggunakan sintaks pembaruan struct seperti ini:
use std::default::Default; #[derive(Debug)] pub struct Sample { a: u32, b: u32, c: u32, } impl Default for Sample { fn default() -> Self { Sample { a: 2, b: 4, c: 6} } } fn main() { let s = Sample { c: 23, ..Sample::default() }; println!("{:?}", s); }
[atas permintaan, saya mem-posting silang jawaban ini dari pertanyaan duplikat]
sumber
Rust tidak mendukung argumen fungsi default, dan saya tidak yakin ini akan diterapkan di masa mendatang. Jadi saya menulis duang proc_macro untuk menerapkannya dalam bentuk makro.
Sebagai contoh:
duang! ( fn add(a: i32 = 1, b: i32 = 2) -> i32 { a + b } ); fn main() { assert_eq!(add!(b=3, a=4), 7); assert_eq!(add!(6), 8); assert_eq!(add(4,5), 9); }
sumber
Jika Anda menggunakan Rust 1.12 atau yang lebih baru, Anda setidaknya dapat membuat argumen fungsi lebih mudah digunakan dengan
Option
daninto()
:fn add<T: Into<Option<u32>>>(a: u32, b: T) -> u32 { if let Some(b) = b.into() { a + b } else { a } } fn main() { assert_eq!(add(3, 4), 7); assert_eq!(add(8, None), 8); }
sumber
Cara lain adalah dengan mendeklarasikan enum dengan parameter opsional sebagai varian, yang dapat dijadikan parameter untuk mengambil jenis yang tepat untuk setiap opsi. Fungsi ini dapat diterapkan untuk mengambil potongan panjang variabel dari varian enum. Mereka bisa dalam urutan dan panjang apapun. Default diimplementasikan dalam fungsi sebagai tugas awal.
enum FooOptions<'a> { Height(f64), Weight(f64), Name(&'a str), } use FooOptions::*; fn foo(args: &[FooOptions]) { let mut height = 1.8; let mut weight = 77.11; let mut name = "unspecified".to_string(); for opt in args { match opt { Height(h) => height = *h, Weight(w) => weight = *w, Name(n) => name = n.to_string(), } } println!(" name: {}\nweight: {} kg\nheight: {} m", name, weight, height); } fn main() { foo( &[ Weight(90.0), Name("Bob") ] ); }
keluaran:
name: Bob weight: 90 kg height: 1.8 m
sumber