Apakah mungkin untuk membuat fungsi dengan argumen default?
fn add(a: int = 1, b: int = 2) { a + b }
Apakah mungkin untuk membuat fungsi dengan argumen default?
fn add(a: int = 1, b: int = 2) { a + b }
Option
dan secara eksplisit meneruskan None
.
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.
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.
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]
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);
}
Jika Anda menggunakan Rust 1.12 atau yang lebih baru, Anda setidaknya dapat membuat argumen fungsi lebih mudah digunakan dengan Option
dan into()
:
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);
}
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