Given a struct that depends on a generic type parameter, can we define an associated function whose implementation depends on that type?
I'd like to pass a struct to a routine, but have the associated functions compute differently depending on the internal type. This routine also depends on the members in the struct, so I'd rather not move everything to a trait.
As an example, the following code attempts to define a different printme function depending on the type involved:
// 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 });
}
This gives the compiler error:
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`
If I move the definition of printme into another trait, we have a similar, but different problem
// 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 });
}
This gives the compiler error:
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<_>`
Strictly speaking, we can fix this experiment by just adding a better trait to the types with:
// 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 });
}
which gives the correct output:
In a f64: 1.2
In a u32: 12
That said, this code is relatively simple, so this kind of fix is easy. More generally, I have a struct that depends on user defined data. This data will necessarily have a different set of associated methods and it would be difficult to force each kind of data to have a common interface. However, the struct that depends on this data can assimilate this information well as long as it knows what kind of data it has. Theoretically, we could define two different structs that accept the two different kinds of data and have these structs implement a common interface. That said, I really want access to a common set of fields and would rather not have to define a number of setters and getters. Is there a better way to accomplish this?
Tthat implements bothMyF64andMyU32, at which point the compiler can't know whichprintmeto use. - Jmb