¿Es posible en Rust crear una función con un argumento predeterminado?
fn add(a: int = 1, b: int = 2) { a + b }
¿Es posible en Rust crear una función con un argumento predeterminado?
fn add(a: int = 1, b: int = 2) { a + b }
Option
y pasar explícitamente None
.
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.
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.
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]
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);
}
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
y 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);
}
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