Rust'ta varsayılan işlev bağımsız değişkenleri


103

Rust'ta varsayılan bağımsız değişkenli bir işlev oluşturmak mümkün müdür?

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

4
# 6973 birkaç çözüm içerir (bir yapı kullanarak).
huon

2020'de nasıl kodlayabilirsiniz?
puentesdiaz

@puentesdias Kabul edilen cevap hala doğru cevaptır. Rust'ta bunu yapmanın bir yolu yok ve ya bir makro yazmalı ya da kullanmalı Optionve açıkça geçirmelisiniz None.
Jeroen

Yanıtlar:


55

Hayır, şu anda değil. Eninde sonunda uygulanacağını düşünüyorum, ancak şu anda bu alanda aktif bir çalışma yok.

Burada kullanılan tipik teknik, farklı adlara ve imzalara sahip işlevleri veya yöntemleri kullanmaktır.


2
@ ner0x652: ancak bu yaklaşımın resmi olarak cesaretinin kırıldığını unutmayın.
Chris Morgan

@ChrisMorgan Bunun resmi olarak cesareti kırılmış bir kaynağınız var mı?
Jeroen

1
@JeroenBollen Birkaç dakika içinde bulabileceğim en iyi arama reddit.com/r/rust/comments/556c0g/… , o sırada Rust proje lideri olan brson gibi insanların olduğu yer. IRC daha fazlasına sahip olabilirdi, emin değilim.
Chris Morgan

108

Varsayılan argümanlar desteklenmediğinden benzer bir davranış elde edebilirsiniz. Option<T>

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

Bu, varsayılan değer ve işlevin yalnızca bir kez kodlanması (her çağrıda yerine) hedefini gerçekleştirir, ancak elbette yazılması çok daha fazla şey vardır. add(None, None)Bakış açınıza bağlı olarak beğenebileceğiniz veya beğenmeyeceğiniz işlev çağrısı gibi görünecektir .

Kodlayıcı potansiyel olarak bir seçim yapmayı unuttuğundan bağımsız değişken listesine hiçbir şey yazmadığınızı görürseniz, buradaki en büyük avantaj açıklıktır; arayan açıkça sizin varsayılan değerinizle gitmek istediğini ve hiçbir şey koymazsa bir derleme hatası alacağını söylüyor. Yazmak olarak düşünün add(DefaultValue, DefaultValue).

Ayrıca bir makro da kullanabilirsiniz:

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

İki çözüm arasındaki en büyük fark, "Seçenek" argümanlarıyla yazmanın tamamen geçerli olmasıdır add(None, Some(4)), ancak makro örüntü eşleşmesiyle yapamazsınız (bu, Python'un varsayılan argüman kurallarına benzer).

Ayrıca bir "bağımsız değişkenler" yapısı ve From/ Intonitelikleri de kullanabilirsiniz :

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

Bu seçim açıkça çok daha fazla koddur, ancak makro tasarımından farklı olarak tür sistemini kullanır, bu da derleyici hatalarının kitaplığınız / API kullanıcınız için daha yararlı olacağı anlamına gelir. Bu aynı zamanda, Fromeğer kendilerine yardımcı olacaksa , kullanıcıların kendi uygulamalarını yapmalarına da olanak tanır .


2
bu cevap, her yaklaşım için bir tane olmak üzere birkaç cevap olarak daha iyi olacaktır. Ben bunlardan sadece birini upvote istiyorum
Joel

56

Hayır, Rust varsayılan işlev bağımsız değişkenlerini desteklemez. Farklı isimlerle farklı yöntemler tanımlamalısınız. Rust, türleri türetmek için işlev adlarını kullandığından (işlev aşırı yükleme bunun tersini gerektirir), çünkü işlev aşırı yüklemesi yoktur.

Yapı başlatma durumunda, yapı güncelleme sözdizimini şu şekilde kullanabilirsiniz:

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

[istek üzerine, bu yanıtı yinelenen bir sorudan çapraz olarak gönderdim]


4
Bu, varsayılan argümanlar için çok kullanışlı bir modeldir. Daha yüksek olmalı
Ben

9

Rust varsayılan işlev argümanlarını desteklemiyor ve gelecekte uygulanacağına da inanmıyorum. Bu yüzden makro formda uygulamak için bir proc_macro duang yazdım .

Örneğin:

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

7

Rust 1.12 veya sonraki bir sürümünü kullanıyorsanız, en azından işlev argümanlarının Optionve ile kullanımını kolaylaştırabilirsiniz 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);
}

7
Teknik olarak doğru olsa da, Rust topluluğu bunun "iyi" bir fikir olup olmadığı konusunda sesli olarak bölünmüştür. Ben şahsen "iyi olmayan" kampına düşüyorum.
Shepmaster

1
@Shepmaster muhtemelen kod boyutunu artırabilir ve süper okunabilir değildir. Bu kalıbı kullanmaya itirazlar bunlar mı? Şimdiye kadar, ödünleşimlerin ergonomik API'lerin hizmetinde değerli olduğunu buldum, ancak diğer bazı sorunları kaçırıyor olabileceğimi düşünürdüm.
squidpickles

2

Diğer bir yol, her seçenek için doğru türü alacak şekilde parametrelendirilebilen, isteğe bağlı parametrelerle değişken olarak bir enum bildirmek olabilir. Fonksiyon, enum varyantlarının değişken uzunluklu bir dilimini almak için uygulanabilir. Herhangi bir sırada ve uzunlukta olabilirler. Varsayılanlar, işlev içinde ilk atamalar olarak uygulanır.

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") ] );

}

çıktı:

  name: Bob
weight: 90 kg
height: 1.8 m
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.