web-dev-qa-db-fra.com

Arguments de fonction par défaut dans Rust

Est-il possible dans Rust de créer une fonction avec un argument par défaut?

fn add(a: int = 1, b: int = 2) { a + b }
55
Jeroen Bollen

Non, ce n'est pas le cas actuellement. Je pense qu'il est probable qu'il sera éventuellement mis en œuvre, mais il n'y a pas de travail actif dans ce domaine à l'heure actuelle.

La technique typique employée ici consiste à utiliser des fonctions ou des méthodes avec différents noms et signatures.

35
Chris Morgan

Étant donné que les arguments par défaut ne sont pas pris en charge, vous pouvez obtenir un comportement similaire en utilisant Option<T>

fn add(a: Option<i32>, b: Option<i32>) -> i32 {
    a.unwrap_or(1) + b.unwrap_or(2)
}

Cela atteint l'objectif d'avoir la valeur par défaut et la fonction codée une seule fois (au lieu de dans chaque appel), mais est bien sûr beaucoup plus à taper. L'appel de fonction ressemblera à add(None, None), que vous aimerez ou non selon votre point de vue.

Si vous voyez ne rien taper dans la liste des arguments comme le codeur oubliant potentiellement de faire un choix, alors le gros avantage ici est dans l'explicitness; l'appelant dit explicitement qu'il veut aller avec votre valeur par défaut, et obtiendra une erreur de compilation s'il ne met rien. Considérez-le comme tapant add(DefaultValue, DefaultValue).

Vous pouvez également utiliser une 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 différence entre les deux solutions est qu'avec les arguments "Option", il est tout à fait valide d'écrire add(None, Some(4)), mais avec le modèle de macro correspondant, vous ne pouvez pas (c'est similaire aux règles d'argument par défaut de Python).

Vous pouvez également utiliser une structure "arguments" et les traits 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)));
}

Ce choix est évidemment beaucoup plus de code, mais contrairement à la conception de macro, il utilise le système de type, ce qui signifie que les erreurs de compilation seront plus utiles à votre utilisateur de bibliothèque/API. Cela permet également aux utilisateurs de créer leur propre implémentation From si cela leur est utile.

48
ampron

Non, Rust ne prend pas en charge les arguments de fonction par défaut. Vous devez définir différentes méthodes avec des noms différents. Il n'y a pas de surcharge de fonction non plus, car Rust use function des noms pour dériver des types (la surcharge de fonction nécessite l'inverse).

En cas d'initialisation de la structure, vous pouvez utiliser la syntaxe de mise à jour de la structure comme ceci:

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);
}

[sur demande, j'ai posté cette réponse à partir d'une question en double]

29
eulerdisk

Si vous utilisez Rust 1.12 ou version ultérieure, vous pouvez au moins rendre les arguments de fonction plus faciles à utiliser avec Option et 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);
}
3
squidpickles

Rust ne prend pas en charge les arguments de fonction par défaut, et je ne pense pas qu'il sera implémenté à l'avenir. J'ai donc écrit un proc_macro duang pour l'implémenter sous la forme macro.

Par exemple:

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);
}
1
zhengmian hu