É possível no Rust criar uma função com um argumento padrão?
fn add(a: int = 1, b: int = 2) { a + b }
É possível no Rust criar uma função com um argumento padrão?
fn add(a: int = 1, b: int = 2) { a + b }
Option
e passar explicitamente None
.
Respostas:
Não, não é no momento. Eu acho que provavelmente será implementado, mas não há nenhum trabalho ativo neste espaço no momento.
A técnica típica empregada aqui é usar funções ou métodos com nomes e assinaturas diferentes.
Uma vez que os argumentos padrão não são suportados, você pode obter um comportamento semelhante usando Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 {
a.unwrap_or(1) + b.unwrap_or(2)
}
Isso cumpre o objetivo de ter o valor padrão e a função codificados apenas uma vez (em vez de em cada chamada), mas é claro que é muito mais para digitar. A chamada de função será semelhante aadd(None, None)
, da qual você pode ou não gostar, dependendo da sua perspectiva.
Se você não perceber nada digitando na lista de argumentos, já que o codificador potencialmente esquece de fazer uma escolha, a grande vantagem aqui está na explicitação; o chamador está dizendo explicitamente que deseja seguir seu valor padrão e obterá um erro de compilação se não inserir nada. Pense nisso como uma digitação add(DefaultValue, DefaultValue)
.
Você também pode usar uma 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);
A grande diferença entre as duas soluções é que com os argumentos -al "Opção" é completamente válido escrever add(None, Some(4))
, mas com a correspondência de padrão de macro você não pode (isso é semelhante às regras de argumento padrão do Python).
Você também pode usar uma estrutura de "argumentos" e os 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)));
}
Esta escolha é obviamente muito mais código, mas ao contrário do design de macro, ela usa o sistema de tipos, o que significa que os erros do compilador serão mais úteis para o usuário de sua biblioteca / API. Isso também permite que os usuários façam sua própria From
implementação, se isso for útil para eles.
Não, Rust não oferece suporte a argumentos de função padrão. Você deve definir métodos diferentes com nomes diferentes. Também não há sobrecarga de função, porque Rust usa nomes de função para derivar tipos (a sobrecarga de função requer o oposto).
No caso de inicialização da estrutura, você pode usar a sintaxe de atualização da estrutura como esta:
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, fiz uma postagem cruzada desta resposta de uma pergunta duplicada]
Se você estiver usando Rust 1.12 ou posterior, você pode pelo menos tornar os argumentos de função mais fáceis de usar com 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);
}
Rust não suporta argumentos de função padrão, e não acredito que será implementado no futuro. Então, escrevi um proc_macro duang para implementá-lo na forma de macro.
Por exemplo:
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);
}
Outra forma poderia ser declarar um enum com os parâmetros opcionais como variantes, que podem ser parametrizados para obter o tipo certo para cada opção. A função pode ser implementada para obter uma fatia de comprimento variável das variantes enum. Eles podem estar em qualquer ordem e comprimento. Os padrões são implementados na função como atribuições iniciais.
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") ] );
}
resultado:
name: Bob
weight: 90 kg
height: 1.8 m