È possibile in Rust creare una funzione con un argomento predefinito?
fn add(a: int = 1, b: int = 2) { a + b }
È possibile in Rust creare una funzione con un argomento predefinito?
fn add(a: int = 1, b: int = 2) { a + b }
Option
e passare esplicitamente None
.
Risposte:
No, al momento non lo è. Penso che sia probabile che alla fine verrà implementato, ma al momento non c'è lavoro attivo in questo spazio.
La tecnica tipica qui impiegata consiste nell'usare funzioni o metodi con nomi e firme differenti.
Poiché gli argomenti predefiniti non sono supportati, puoi ottenere un comportamento simile usando Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 {
a.unwrap_or(1) + b.unwrap_or(2)
}
Questo raggiunge l'obiettivo di avere il valore predefinito e la funzione codificati solo una volta (invece che in ogni chiamata), ma ovviamente è molto di più da digitare. La chiamata alla funzione avrà un aspetto add(None, None)
, che potrebbe piacerti o meno a seconda della tua prospettiva.
Se vedi che non digita nulla nell'elenco degli argomenti poiché il programmatore si dimentica potenzialmente di fare una scelta, allora il grande vantaggio qui è nell'esplicita '; il chiamante sta dicendo esplicitamente che vuole andare con il tuo valore predefinito e riceverà un errore di compilazione se non inserisce nulla. Consideralo come una digitazione add(DefaultValue, DefaultValue)
.
Puoi anche usare una macro:
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);
La grande differenza tra le due soluzioni è che con gli argomenti "Opzione" è completamente valido da scrivere add(None, Some(4))
, ma con la corrispondenza del modello macro non è possibile (questo è simile alle regole degli argomenti predefinite di Python).
Puoi anche usare una struttura "argomenti" e i tratti From
/ Into
:
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)));
}
Questa scelta è ovviamente molto più codice, ma a differenza del design macro utilizza il sistema dei tipi, il che significa che gli errori del compilatore saranno più utili per l'utente della libreria / API. Ciò consente inoltre agli utenti di realizzare la propria From
implementazione se ciò è loro utile.
No, Rust non supporta gli argomenti delle funzioni predefinite. Devi definire metodi diversi con nomi diversi. Non esiste nemmeno il sovraccarico di funzioni, perché Rust usa i nomi delle funzioni per derivare i tipi (il sovraccarico di funzioni richiede l'opposto).
In caso di inizializzazione della struttura è possibile utilizzare la sintassi di aggiornamento della struttura in questo modo:
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);
}
[su richiesta, ho incrociato questa risposta da una domanda duplicata]
Rust non supporta gli argomenti delle funzioni predefinite e non credo che verrà implementato in futuro. Quindi ho scritto un duang proc_macro per implementarlo nella forma macro.
Per esempio:
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);
}
Se stai usando Rust 1.12 o successivo, puoi almeno rendere gli argomenti delle funzioni più facili da usare con Option
e 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);
}
Un altro modo potrebbe essere quello di dichiarare un'enumerazione con i parametri opzionali come varianti, che possono essere parametrizzati per prendere il tipo giusto per ciascuna opzione. La funzione può essere implementata per prendere una fetta di lunghezza variabile delle varianti enum. Possono essere in qualsiasi ordine e lunghezza. I valori predefiniti vengono implementati all'interno della funzione come assegnazioni iniziali.
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") ] );
}
produzione:
name: Bob
weight: 90 kg
height: 1.8 m