当前位置: 首页 > 知识库问答 >
问题:

如何为依赖于 Rust 中的泛型类型参数的结构的关联函数定义不同的实现?

杜元明
2023-03-14

给定一个依赖于泛型类型参数的结构,我们可以定义一个实现依赖于该类型的关联函数吗?

我想将结构传递给例程,但根据内部类型,让关联的函数以不同的方式计算。这个例程还取决于结构中的成员,所以我宁愿不把所有东西都移到一个特质上。

例如,以下代码尝试根据所涉及的类型定义不同的printme函数:

// Trait locked to a type
trait MyF64 {}
impl MyF64 for f64 {}
trait MyU32 {}
impl MyU32 for u32 {}

// Some kind of struct
struct Foo<T> {
    t: T,
}

// Implementation for f64
impl<T> Foo<T>
where
    T: MyF64,
{
    fn printme(&self) {
        println!("In a f64: {}", self.t);
    }
}

// Implementation for u32
impl<T> Foo<T>
where
    T: MyU32,
{
    fn printme(&self) {
        println!("In a u32: {}", self.t);
    }
}

// Takes a foo
fn foo<T>(x: Foo<T>) {
    foo.printme();
}

fn main() {
    // Try both cases
    foo(Foo { t: 1.2 });
    foo(Foo { t: 12 });
}

这会导致编译器错误:

error[E0592]: duplicate definitions with name `printme`
  --> src/main.rs:17:5
   |
17 | /     fn printme(&self) {
18 | |         println!("In a f64: {}", self.t);
19 | |     }
   | |_____^ duplicate definitions for `printme`
...
27 | /     fn printme(&self) {
28 | |         println!("In a u32: {}", self.t);
29 | |     }
   | |_____- other definition for `printme`

如果我将printme的定义转移到另一个特征中,我们就会遇到一个类似但不同的问题。

// Trait locked to a type
trait MyF64 {}
impl MyF64 for f64 {}
trait MyU32 {}
impl MyU32 for u32 {}

// Some kind of struct
struct Foo<T> {
    t: T,
}

// Trait for Foo
trait FooTrait {
    fn printme(&self);
}

// Implementation for f64
impl<T> FooTrait for Foo<T>
where
    T: MyF64,
{
    fn printme(&self) {
        println!("In a f64: {}", self.t);
    }
}

// Implementation for u32
impl<T> FooTrait for Foo<T>
where
    T: MyU32,
{
    fn printme(&self) {
        println!("In a u32: {}", self.t);
    }
}

// Takes a foo
fn foo<T>(x: Foo<T>)
where
    Foo<T>: FooTrait,
{
    foo.printme();
}

fn main() {
    // Try both cases
    foo(Foo { t: 1.2 });
    foo(Foo { t: 12 });
}

这会导致编译器错误:

error[E0119]: conflicting implementations of trait `FooTrait` for type `Foo<_>`:
  --> src/main.rs:28:1
   |
18 | / impl<T> FooTrait for Foo<T>
19 | | where
20 | |     T: MyF64,
21 | | {
...  |
24 | |     }
25 | | }
   | |_- first implementation here
...
28 | / impl<T> FooTrait for Foo<T>
29 | | where
30 | |     T: MyU32,
31 | | {
...  |
34 | |     }
35 | | }
   | |_^ conflicting implementation for `Foo<_>`

严格来说,我们可以通过向类型添加更好的特征来修复这个实验:

// External libraries
use std::fmt::Display;

// Trait gives the name
trait MyTrait {
    fn name(&self) -> String;
}
impl MyTrait for f64 {
    fn name(&self) -> String {
        "f64".to_string()
    }
}
impl MyTrait for u32 {
    fn name(&self) -> String {
        "u32".to_string()
    }
}

// Some kind of struct
struct Foo<T> {
    t: T,
}
impl<T> Foo<T>
where
    T: MyTrait + Display,
{
    fn printme(&self) {
        println!("In a {}: {}", self.t.name(), self.t);
    }
}

// Takes a foo
fn foo<T>(x: Foo<T>)
where
    T: MyTrait + Display,
{
    x.printme();
}

fn main() {
    // Try both cases
    foo(Foo { t: 1.2 });
    foo(Foo { t: 12 });
}

给出正确的输出:

In a f64: 1.2
In a u32: 12

也就是说,此代码相对简单,因此这种修复很容易。更一般地说,我有一个依赖于用户定义数据的结构。此数据必然具有一组不同的关联方法,并且很难强制每种数据具有通用接口。但是,依赖于此数据的结构可以很好地吸收此信息,只要它知道它拥有哪种数据。从理论上讲,我们可以定义两种不同的结构,接受两种不同类型的数据,并让这些结构实现一个通用的接口。也就是说,我真的希望访问一组通用的字段,并且宁愿不必定义许多 setter 和 getter。有没有更好的方法来实现这一目标?

共有1个答案

井逸明
2023-03-14

好吧,我想我应该考虑更长的时间,但是以下内容实现了我在没有二传手和获取器的情况下想要的:

// Trait locked to a type
trait Float {
    fn bar(&self) -> String;
}
impl Float for f32 {
    fn bar(&self) -> String {
        "f32".to_string()
    }
}
impl Float for f64 {
    fn bar(&self) -> String {
        "f64".to_string()
    }
}
trait Int {
    fn baz(&self) -> &'static str;
}
impl Int for i32 {
    fn baz(&self) -> &'static str {
        "i32"
    }
}
impl Int for i64 {
    fn baz(&self) -> &'static str {
        "i64"
    }
}

// Define all of the different implementations that we care to implement in Foo
enum MyTypes {
    Float(Box <dyn Float>),
    Int(Box <dyn Int>),
}

// Some kind of struct
struct Foo {
    t : MyTypes,
}

// Implementation for f64
impl Foo {
    fn printme(&self) {
        match self.t {
            MyTypes::Float(ref t) => {
                println!("In a Float({})", t.bar());
            }
            MyTypes::Int(ref t) => {
                println!("In an Int({})", t.baz());
            }
        }
    }
}

// Takes a foo
fn foo(x : Foo) {
    x.printme();
}

fn main() {
    // Try both cases
    foo(Foo { t : MyTypes::Float(Box::new(1.2_f32))});
    foo(Foo { t : MyTypes::Int(Box::new(12_i64))});
}

从本质上讲,如果我们知道用户定义的数据将来自有限数量的特征,我们可以将所有内容包装在枚举中,然后让结构根据用户提供的数据类型更改其实现。这需要根据特征对传入的用户数据进行装箱,从而适当地隐藏基础类型。

 类似资料:
  • 问题内容: 我有一个参数化的类: 致电: 那么,如何使用Java泛型来获取实际的类型? 问题答案: 可以做到,但是类型擦除可能会很困难。正如其他答案所讨论的那样,您必须成为的子类或将类型的字段添加到中,而执行此操作所需的反射代码却很复杂。 在这种情况下,我建议您解决以下问题: 由于Java对静态方法的类型推断,因此无需太多额外的样板即可构造类: 这样,您就具有完全的通用性和类型安全性,并且仍然可以

  • 我不想为每个类型T编写这个方法只是为了调用getMessage()并将其传递给下一个方法。 有可能写出这样的方法吗?我只想访问ConstraintViolation接口的方法,这些方法不依赖于类型T(如字符串getMessage())。

  • 问题内容: 如果我有一个像这样的抽象类: 还有一些从Item派生的类是这样的: 我不明白为什么我不能使用泛型调用构造函数: 我知道可以有一个没有构造函数的类型,但是这种情况是不可能的,因为Pencil具有没有参数的构造函数,而Item是抽象的。但是我从eclipse中得到了这个错误: 无法实例化 我不明白为什么的 T类型 ,以及如何避免这种情况? 问题答案: 无法使用Java类型系统来强制类层次结

  • 我试图编写一个函数来返回

  • 假设我有以下课程: 我想添加一个构造函数,它需要一个

  • 我正在做作业,所以我只想修复我的编译错误,这样我就可以继续工作了。我需要创建一个PointList类,在ArrayList中保存一个Point对象列表。PointList类应该接受任何作为Point类实例或Point子类的对象。 我不断收到一个编译器错误,上面写着 我真的不明白我错过了什么,我已经通读了这本书,似乎不明白为什么我会得到这个错误。我已经制作了 Point 类并完成了测试,但似乎无法编