我正在建模一个API,其中方法重载是一个很好的选择。我天真的尝试失败了:
// fn attempt_1(x: int) {}
// fn attempt_1(x: f32) {}
// Error: duplicate definition of value `attempt_1`
然后我添加了一个枚举并完成了:
enum IntOrFloat {
Int(int),
Float(f32),
}
fn attempt_2(x: IntOrFloat) {}
fn main() {
let i: int = 1;
let f: f32 = 3.0;
// Can't pass the value directly
// attempt_2(i);
// attempt_2(f);
// Error: mismatched types: expected `IntOrFloat`
attempt_2(Int(i));
attempt_2(Float(f));
// Ugly that the caller has to explicitly wrap the parameter
}
进行一些快速搜索,我found some references谈论重载,所有这些似乎都以“我们不会允许这样做,但是尝试一下特质“。所以我试过了:
enum IntOrFloat {
Int(int),
Float(f32),
}
trait IntOrFloatTrait {
fn to_int_or_float(&self) -> IntOrFloat;
}
impl IntOrFloatTrait for int {
fn to_int_or_float(&self) -> IntOrFloat { Int(*self) }
}
impl IntOrFloatTrait for f32 {
fn to_int_or_float(&self) -> IntOrFloat { Float(*self) }
}
fn attempt_3(x: &IntOrFloatTrait) {}
fn main() {
let i: int = 1;
let f: f32 = 3.0;
attempt_3(&i);
attempt_3(&f);
// Better, but the caller still has to explicitly take the reference
}
这是我能接近方法重载的最接近的吗?有更清洁的方式吗?
答案 0 :(得分:8)
是的,有,你几乎已经得到它了。特征是要走的路,但你不需要特质对象,使用泛型:
#[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);
}
看到它正常工作here。
答案 1 :(得分:1)
这是丢弃enum
的另一种方法。这是弗拉基米尔回答的一个重复。
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);
}
答案 2 :(得分:1)
函数重载是可能的!!! (好吧,有点……)
此 Rust Playground example 有更详细的示例,并显示了结构变体的用法,这可能更适合作为参数文档。
对于更严重的灵活重载,您希望拥有任意数量的任何类型的参数集,您可以利用 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 !!
}
首先,将不同的参数组合定义为一个枚举:
// The variants should consist of unambiguous sets of types.
enum FooParam {
Bar(i32, i32),
Baz(f32),
Qux(&'static str, i32, f64),
}
现在,转换代码;可以编写一个宏来执行乏味的 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)
}
}
最后,用泛型方法实现结构体:
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)
}
}
}
}
注意:需要指定 T
上的 trait bound。
此外,变体需要由编译器不会发现歧义的类型组合组成 - 这也是其他语言(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<...>.