Ist es in Rust möglich, eine Funktion mit einem Standardargument zu erstellen?
fn add(a: int = 1, b: int = 2) { a + b }
Option
und explizit übergeben None
.
Ist es in Rust möglich, eine Funktion mit einem Standardargument zu erstellen?
fn add(a: int = 1, b: int = 2) { a + b }
Option
und explizit übergeben None
.
Antworten:
Nein, das ist derzeit nicht der Fall. Ich halte es für wahrscheinlich, dass es irgendwann implementiert wird, aber es gibt derzeit keine aktive Arbeit in diesem Bereich.
Die hier verwendete typische Technik besteht darin, Funktionen oder Methoden mit unterschiedlichen Namen und Signaturen zu verwenden.
Da Standardargumente nicht unterstützt werden, können Sie mit ein ähnliches Verhalten erzielen Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 {
a.unwrap_or(1) + b.unwrap_or(2)
}
Dies erreicht das Ziel, den Standardwert und die Funktion nur einmal (statt bei jedem Aufruf) codieren zu lassen, ist aber natürlich noch viel mehr zu tippen. Der Funktionsaufruf sieht so aus add(None, None)
, wie Sie es je nach Perspektive mögen oder nicht.
Wenn Sie sehen, dass Sie nichts in die Argumentliste eingeben, da der Codierer möglicherweise vergisst, eine Auswahl zu treffen, liegt der große Vorteil hier in der expliziten Aussage. Der Aufrufer sagt ausdrücklich, dass er mit Ihrem Standardwert arbeiten möchte, und erhält einen Kompilierungsfehler, wenn er nichts einfügt. Betrachten Sie es als Tippen add(DefaultValue, DefaultValue)
.
Sie können auch ein Makro verwenden:
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);
Der große Unterschied zwischen den beiden Lösungen besteht darin, dass das Schreiben mit "Option" -Al-Argumenten vollständig gültig add(None, Some(4))
ist, mit dem Makromuster-Matching jedoch nicht (dies ähnelt den Standardargumentregeln von Python).
Sie können auch eine "Argumente" -Struktur und die From
/ Into
Merkmale verwenden:
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)));
}
Diese Auswahl ist offensichtlich viel mehr Code, aber im Gegensatz zum Makrodesign wird das Typsystem verwendet, was bedeutet, dass die Compilerfehler für Ihren Bibliotheks- / API-Benutzer hilfreicher sind. Auf diese Weise können Benutzer auch ihre eigene From
Implementierung vornehmen , wenn dies für sie hilfreich ist.
Nein, Rust unterstützt keine Standardfunktionsargumente. Sie müssen verschiedene Methoden mit unterschiedlichen Namen definieren. Es gibt auch keine Funktionsüberladung, da Rust Funktionsnamen verwendet, um Typen abzuleiten (Funktionsüberladung erfordert das Gegenteil).
Im Falle einer Strukturinitialisierung können Sie die Strukturaktualisierungssyntax wie folgt verwenden:
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);
}
[Auf Anfrage habe ich diese Antwort von einer doppelten Frage abgekreuzt]
Wenn Sie mit Rust 1.12 oder höher verwenden , können Sie zumindest Schliesser Argumente einfacher zu bedienen mit Option
und 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);
}
Rust unterstützt keine Standardfunktionsargumente und ich glaube nicht, dass es in Zukunft implementiert wird. Also habe ich ein proc_macro duang geschrieben , um es in der Makroform zu implementieren.
Beispielsweise:
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);
}
Eine andere Möglichkeit könnte darin bestehen, eine Aufzählung mit den optionalen Parametern als Varianten zu deklarieren, die so parametrisiert werden können, dass für jede Option der richtige Typ verwendet wird. Die Funktion kann implementiert werden, um einen Slice variabler Länge der Enum-Varianten zu nehmen. Sie können in beliebiger Reihenfolge und Länge sein. Die Standardeinstellungen werden innerhalb der Funktion als Erstzuweisungen implementiert.
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") ] );
}
Ausgabe:
name: Bob
weight: 90 kg
height: 1.8 m