¿Es posible en Rust crear una función con un argumento predeterminado?
fn add(a: int = 1, b: int = 2) { a + b }
function
parameters
arguments
rust
Jeroen
fuente
fuente
Option
y pasar explícitamenteNone
.Respuestas:
No, no lo es en la actualidad. Creo que es probable que eventualmente se implemente, pero no hay trabajo activo en este espacio en la actualidad.
La técnica típica empleada aquí es utilizar funciones o métodos con diferentes nombres y firmas.
fuente
Dado que los argumentos predeterminados no son compatibles, puede obtener un comportamiento similar usando
Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 { a.unwrap_or(1) + b.unwrap_or(2) }
Esto logra el objetivo de tener el valor predeterminado y la función codificada solo una vez (en lugar de en cada llamada), pero, por supuesto, hay mucho más para escribir. La llamada a la función se verá así
add(None, None)
, lo que puede que le guste o no según su perspectiva.Si ve que no escribe nada en la lista de argumentos, ya que el codificador se olvida potencialmente de hacer una elección, entonces la gran ventaja aquí es que es explícito; la persona que llama está diciendo explícitamente que quiere ir con su valor predeterminado y obtendrá un error de compilación si no pone nada. Piense en ello como escribir
add(DefaultValue, DefaultValue)
.También puedes usar 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 gran diferencia entre las dos soluciones es que con los argumentos de "Opción" -al es completamente válido escribir
add(None, Some(4))
, pero con el patrón de macro coincidente no puede (esto es similar a las reglas de argumentos predeterminadas de Python).También puedes usar una estructura de "argumentos" y los rasgos
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))); }
Esta elección es obviamente mucho más código, pero a diferencia del diseño de macros, utiliza el sistema de tipos, lo que significa que los errores del compilador serán más útiles para el usuario de la biblioteca / API. Esto también permite a los usuarios realizar su propia
From
implementación si les resulta útil.fuente
No, Rust no admite argumentos de función predeterminados. Tienes que definir diferentes métodos con diferentes nombres. Tampoco hay sobrecarga de funciones, porque Rust usa nombres de funciones para derivar tipos (la sobrecarga de funciones requiere lo contrario).
En caso de inicialización de la estructura, puede usar la sintaxis de actualización de la estructura de la siguiente manera:
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); }
[a pedido, publiqué esta respuesta de una pregunta duplicada]
fuente
Rust no admite argumentos de función predeterminados y no creo que se implemente en el futuro. Así que escribí un proc_macro duang para implementarlo en forma de macro.
Por ejemplo:
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); }
fuente
Si está utilizando Rust 1.12 o posterior, al menos puede hacer que los argumentos de función sean más fáciles de usar con
Option
yinto()
: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); }
fuente
Otra forma podría ser declarar una enumeración con los parámetros opcionales como variantes, que se pueden parametrizar para tomar el tipo correcto para cada opción. La función se puede implementar para tomar un segmento de longitud variable de las variantes de enumeración. Pueden estar en cualquier orden y longitud. Los valores predeterminados se implementan dentro de la función como asignaciones iniciales.
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") ] ); }
salida:
name: Bob weight: 90 kg height: 1.8 m
fuente