Czy można utworzyć funkcję z domyślnym argumentem?
fn add(a: int = 1, b: int = 2) { a + b }
function
parameters
arguments
rust
Jeroen
źródło
źródło
Option
i jawnie przekazaćNone
.Odpowiedzi:
Nie, obecnie nie ma. Myślę, że jest prawdopodobne, że w końcu zostanie wdrożony, ale obecnie nie ma aktywnej pracy w tej przestrzeni.
Typową techniką stosowaną tutaj jest użycie funkcji lub metod o różnych nazwach i podpisach.
źródło
Ponieważ domyślne argumenty nie są obsługiwane, możesz uzyskać podobne zachowanie używając
Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 { a.unwrap_or(1) + b.unwrap_or(2) }
Osiąga to cel polegający na tym, że wartość domyślna i funkcja są kodowane tylko raz (zamiast w każdym wywołaniu), ale oczywiście jest o wiele więcej do wpisania. Wywołanie funkcji będzie wyglądać
add(None, None)
, co może ci się spodobać lub nie, w zależności od twojej perspektywy.Jeśli widzisz, że nic nie wpisuje się na liście argumentów, ponieważ koder potencjalnie zapomina o dokonaniu wyboru, to dużą zaletą jest tutaj jawność; wywołujący wyraźnie mówi, że chce korzystać z domyślnej wartości i otrzyma błąd kompilacji, jeśli nic nie poda. Pomyśl o tym jak o pisaniu
add(DefaultValue, DefaultValue)
.Możesz także użyć makra:
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);
Duża różnica między tymi dwoma rozwiązaniami polega na tym, że w przypadku argumentów -al „Opcja” zapisywanie jest całkowicie poprawne
add(None, Some(4))
, ale przy dopasowaniu wzorca makra nie można (jest to podobne do domyślnych reguł argumentów Pythona).Możesz także użyć struktury „arguments” i
From
/Into
traits: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))); }
Ten wybór to oczywiście o wiele więcej kodu, ale w przeciwieństwie do projektu makra wykorzystuje system typów, co oznacza, że błędy kompilatora będą bardziej pomocne dla użytkownika biblioteki / API. Pozwala to również użytkownikom na tworzenie własnych
From
implementacji, jeśli jest to dla nich pomocne.źródło
Nie, Rust nie obsługuje domyślnych argumentów funkcji. Musisz zdefiniować różne metody o różnych nazwach. Nie ma też przeciążania funkcji, ponieważ Rust używa nazw funkcji do wyprowadzania typów (przeciążanie funkcji wymaga czegoś przeciwnego).
W przypadku inicjalizacji struktury możesz użyć składni aktualizacji struktury w następujący sposób:
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); }
[na życzenie przesłałem tę odpowiedź z drugiego pytania]
źródło
Rust nie obsługuje domyślnych argumentów funkcji i nie wierzę, że zostanie zaimplementowany w przyszłości. Napisałem więc proc_macro duang, aby zaimplementować go w postaci makra.
Na przykład:
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); }
źródło
Jeśli używasz Rust 1.12 lub nowszego, możesz przynajmniej ułatwić używanie argumentów funkcji z
Option
iinto()
: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); }
źródło
Innym sposobem mogłoby być zadeklarowanie wyliczenia z opcjonalnymi parametrami jako wariantami, które można sparametryzować tak, aby przyjmowały odpowiedni typ dla każdej opcji. Funkcję można zaimplementować w celu pobrania wycinka o zmiennej długości wariantów wyliczeniowych. Mogą być w dowolnej kolejności i długości. Wartości domyślne są implementowane w funkcji jako przypisania początkowe.
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") ] ); }
wynik:
name: Bob weight: 90 kg height: 1.8 m
źródło