rust - 如何为依赖于 Rust 中的泛型类型参数的结构定义关联函数的不同实现?
问题描述
给定一个依赖于泛型类型参数的结构,我们可以定义一个实现依赖于该类型的关联函数吗?
我想将结构传递给例程,但相关函数的计算方式取决于内部类型。此例程还取决于结构中的成员,因此我宁愿不要将所有内容都移至特征。
例如,以下代码尝试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。有没有更好的方法来实现这一点?
解决方案
好吧,我想我应该考虑更长的时间,但是以下内容在没有 setter 和 getter 的情况下完成了我想要的:
// 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))});
}
本质上,如果我们知道用户定义的数据将来自有限数量的特征,我们可以将所有内容包装在一个中enum
,然后让结构根据用户提供的数据类型更改其实现。这需要根据 trait 对传入的用户数据进行装箱,从而适当地隐藏底层类型。
推荐阅读
- python - 用于匹配子字符串的正则表达式
- reactjs - createBottomTabNavigator 保持安装堆栈
- javascript - 如何防止按键重复被触发 - JavaScript/jQuery
- java - 创建的通知android未显示在手机中
- excel - 运行时错误“1004”方法范围类的自动筛选失败
- javascript - 如何在 Leaflet 中单击时返回有关 geojson 多边形的详细信息?
- python - spark-submit 作业在集群模式下不起作用
- python - SQLAlchemy 查询 API 无法与提示一起正常工作
- java - 解析具有 JSON 格式列的外部文件
- dart - 带有动画儿童尺寸的 Flutter PageView.builder