我如何近似方法重载?

She*_*ter 8 overloading rust

我正在建模一个API,其中方法重载将是一个很好的选择.我天真的尝试失败了:

// fn attempt_1(_x: i32) {}
// fn attempt_1(_x: f32) {}
// Error: duplicate definition of value `attempt_1`
Run Code Online (Sandbox Code Playgroud)

然后我添加了一个枚举并完成了:

enum IntOrFloat {
    Int(i32),
    Float(f32),
}

fn attempt_2(_x: IntOrFloat) {}

fn main() {
    let i: i32 = 1;
    let f: f32 = 3.0;

    // Can't pass the value directly
    // attempt_2(i);
    // attempt_2(f);
    // Error: mismatched types: expected enum `IntOrFloat`

    attempt_2(IntOrFloat::Int(i));
    attempt_2(IntOrFloat::Float(f));
    // Ugly that the caller has to explicitly wrap the parameter
}
Run Code Online (Sandbox Code Playgroud)

做了一些快速搜索,我发现了 一些 关于重载的引用,所有这些引用似乎都以"我们不会允许这个,但尝试一下"来结束.所以我尝试过:

enum IntOrFloat {
    Int(i32),
    Float(f32),
}

trait IntOrFloatTrait {
    fn to_int_or_float(&self) -> IntOrFloat;
}

impl IntOrFloatTrait for i32 {
    fn to_int_or_float(&self) -> IntOrFloat {
        IntOrFloat::Int(*self)
    }
}

impl IntOrFloatTrait for f32 {
    fn to_int_or_float(&self) -> IntOrFloat {
        IntOrFloat::Float(*self)
    }
}

fn attempt_3(_x: &dyn IntOrFloatTrait) {}

fn main() {
    let i: i32 = 1;
    let f: f32 = 3.0;

    attempt_3(&i);
    attempt_3(&f);
    // Better, but the caller still has to explicitly take the reference
}
Run Code Online (Sandbox Code Playgroud)

这是我最接近方法重载的方法吗?有更干净的方式吗?

Vla*_*eev 10

是的,有,你几乎已经得到它了.特征是要走的路,但你不需要特质对象,使用泛型:

#[derive(Debug)]
enum IntOrFloat {
    Int(i32),
    Float(f32),
}

trait IntOrFloatTrait {
    fn to_int_or_float(&self) -> IntOrFloat;
}

impl IntOrFloatTrait for i32 {
    fn to_int_or_float(&self) -> IntOrFloat {
        IntOrFloat::Int(*self)
    }
}

impl IntOrFloatTrait for f32 {
    fn to_int_or_float(&self) -> IntOrFloat {
        IntOrFloat::Float(*self)
    }
}

fn attempt_4<T: IntOrFloatTrait>(x: T) {
    let v = x.to_int_or_float();
    println!("{:?}", v);
}

fn main() {
    let i: i32 = 1;
    let f: f32 = 3.0;

    attempt_4(i);
    attempt_4(f);
}
Run Code Online (Sandbox Code Playgroud)

看到它在这里工作.


Joe*_*ley 6

这是另一种删除enum. 这是 Vladimir 答案的迭代。

trait Tr {
  fn go(&self) -> ();
}

impl Tr for i32 {
  fn go(&self) {
    println!("i32")
  }
}

impl Tr for f32 {
  fn go(&self) {
    println!("f32")
  }
}

fn attempt_1<T: Tr>(t: T) {
  t.go()
}

fn main() {
  attempt_1(1 as i32);
  attempt_1(1 as f32);
}
Run Code Online (Sandbox Code Playgroud)


Tod*_*odd 5

函数重载是可能的! (嗯,有点……)

这个Rust Playground 示例有更详细的示例,并显示了结构变体的用法,这可能更适合参数文档。

对于更严重的灵活重载,您想要拥有任意数量的任何类型的参数集,您可以利用将From<T>元组转换为枚举变体的特征,并使用一个泛型函数将传递给它的元组转换为枚举类型。

所以这样的代码是可能的:

fn main() {
    let f = Foo { };
    f.do_something(3.14);               // One f32.
    f.do_something((1, 2));             // Two i32's...
    f.do_something(("Yay!", 42, 3.14)); // A str, i32, and f64 !!
}
Run Code Online (Sandbox Code Playgroud)

首先,将不同的参数组合集定义为枚举:

// The variants should consist of unambiguous sets of types.
enum FooParam {
    Bar(i32, i32),
    Baz(f32),
    Qux(&'static str, i32, f64),
}
Run Code Online (Sandbox Code Playgroud)

现在,转换代码;可以编写一个宏来完成繁琐的From<T>实现,但它可以产生以下结果:

impl From<(i32, i32)> for FooParam {
    fn from(p: (i32, i32)) -> Self {
        FooParam::Bar(p.0, p.1)
    }
}
impl From<f32> for FooParam {
    fn from(p: f32) -> Self {
        FooParam::Baz(p)
    }
}
impl From<(&'static str, i32, f64)> for FooParam {
    fn from(p: (&'static str, i32, f64)) -> Self {
        FooParam::Qux(p.0, p.1, p.2)
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,使用泛型方法实现该结构:

struct Foo {}

impl Foo {
    fn do_something<T: Into<FooParam>>(&self, t: T) {
        use FooParam::*;
        let fp = t.into();
        match fp {
            Bar(a, b)    => print!("Bar: {:?}, {:?}\n", a, b),
            Baz(a)       => print!("Baz: {:?}\n", a),
            Qux(a, b, c) => {
                print!("Qux: {:?}, {:?}, {:?}\n", a, b, c)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

T注意:需要指定绑定的特征。

此外,变体需要由编译器不会发现歧义的类型组合组成 - 这也是其他语言(Java/C++)中重载方法的期望。

这种方法有可能性......如果有一个可用的装饰器,或者编写一个From<T>在应用于枚举时自动执行实现的装饰器,那就太棒了。像这样的东西:

// THIS DOESN'T EXIST - so don't expect the following to work.
// This is just an example of a macro that could be written to
// help in using the above approach to function overloading.

#[derive(ParameterOverloads)]
enum FooParam {
    Bar(i32, i32),
    Baz(f32),
    Qux(&'static str, i32, f64),
}

// If this were written, it could eliminate the tedious
// implementations of From<...>.
Run Code Online (Sandbox Code Playgroud)