How to instantiate the same struct with different implementations of a trait? - struct

I have a trait that is implemented by the same struct in different ways. In order to accomplish this I have different structs that have different implementations. For abstraction I'm going to call these structs A-Z:
trait Trait {
fn bar<T: Trait> (&self, z: &Struct3<T>) -> Struct3<T>;
}
struct StructA {
name: String,
color: String,
}
impl Trait for StructA {
fn bar<T: Trait> (&self, z: &Struct3<T>) -> Struct3<T>{
let mut list = Vec::new();
list.push(self.clone());
let y = Struct2::<T> {
name: z.y.name,
list: list,
};
Struct3::<T> {
point: 1,
y: y,
}
}
}
struct StructZ {
name: String,
color: String,
}
impl Trait for StructZ {
fn bar<T: Trait> (&self, z: &Struct3<T>) -> Struct3<T>{
let mut list = Vec::new();
list.push(self.clone());
let y = Struct2::<T> {
name: z.y.name,
list: list,
};
Struct3::<T> {
point: 26,
y: y,
}
}
}
Is there another way to approach this so that each instance of the struct has a different implementation of the trait, or is making a new struct the best way to go?
I am new to compiled languages. Most of the work I have done has been using Python and TypeScript.

One way to do that is to make your trait generic over some type (even if this type is actually not used inside of the trait).
trait Trait<T> { /* ... */ }
That enables you to implement it multiple times for the same struct.
impl Trait<bool> for Struct { /* ... */ }
impl Trait<u32> for Struct { /* ... */ }
Here, bool and u32 just become compile time flags to chose the implementation of Trait that you want to use.
let struct_3a = Trait::<bool>::bar(&my_object, /* ... */);
let struct_3b = Trait::<u32>::bar(&my_object, /* ... */);
I used bool and u32 here, but you can use your own types here to make everything clearer.

Related

Late type in Rust

I'm working with two crates: A and B. I control both. I'd like to create a struct in A that has a field whose type is known only to B (i.e., A is independent of B, but B is dependent on A).
crate_a:
#[derive(Clone)]
pub struct Thing {
pub foo: i32,
pub bar: *const i32,
}
impl Thing {
fn new(x: i32) -> Self {
Thing { foo: x, bar: &0 }
}
}
crate_b:
struct Value {};
fn func1() {
let mut x = A::Thing::new(1);
let y = Value {};
x.bar = &y as *const Value as *const i32;
...
}
fn func2() {
...
let y = unsafe { &*(x.bar as *const Value) };
...
}
This works, but it doesn't feel very "rusty". Is there a cleaner way to do this? I thought about using a trait object, but ran into issues with Clone.
Note: My reason for splitting these out is that the dependencies in B make compilation very slow. Value above is actually from llvm_sys. I'd rather not leak that into A, which has no other dependency on llvm.
The standard way to implement something like this is with generics, which are kind of like type variables: they can be "assigned" a particular type, possibly within some constraints. This is how the standard library can provide types like Vec that work with types that you declare in your crate.
Basically, generics allow Thing to be defined in terms of "some unknown type that will become known later when this type is actually used."
Given the example in your code, it looks like Thing's bar field may or may not be set, which suggests that the built-in Option enum should be used. All you have to do is put a type parameter on Thing and pass that through to Option, like so:
pub mod A {
#[derive(Clone)]
pub struct Thing<T> {
pub foo: i32,
pub bar: Option<T>,
}
impl<T> Thing<T> {
pub fn new(x: i32) -> Self {
Thing { foo: x, bar: None }
}
}
}
pub mod B {
use crate::A;
struct Value;
fn func1() {
let mut x = A::Thing::new(1);
let y = Value;
x.bar = Some(y);
// ...
}
fn func2(x: &A::Thing<Value>) {
// ...
let y: &Value = x.bar.as_ref().unwrap();
// ...
}
}
(Playground)
Here, the x in B::func1() has the type Thing<Value>. You can see with this syntax how Value is substituted for T, which makes the bar field Option<Value>.
If Thing's bar isn't actually supposed to be optional, just write pub bar: T instead, and accept a T in Thing::new() to initialize it:
pub mod A {
#[derive(Clone)]
pub struct Thing<T> {
pub foo: i32,
pub bar: T,
}
impl<T> Thing<T> {
pub fn new(x: i32, y: T) -> Self {
Thing { foo: x, bar: y }
}
}
}
pub mod B {
use crate::A;
struct Value;
fn func1() {
let mut x = A::Thing::new(1, Value);
// ...
}
fn func2(x: &A::Thing<Value>) {
// ...
let y: &Value = &x.bar;
// ...
}
}
(Playground)
Note that the definition of Thing in both of these cases doesn't actually require that T implement Clone; however, Thing<T> will only implement Clone if T also does. #[derive(Clone)] will generate an implementation like:
impl<T> Clone for Thing<T> where T: Clone { /* ... */ }
This can allow your type to be more flexible -- it can now be used in contexts that don't require T to implement Clone, while also being cloneable when T does implement Clone. You get the best of both worlds this way.

How do I 'force' structs to implement the same traits?

I have the following:
pub struct OpBStruct {
title: String,
output_vale: i32,
}
impl OpBStruct {
pub fn new_OpB(in_title: String, in_output_vale: i32) -> OpBStruct {
OpBStruct {
title: in_title,
output_vale: in_output_vale,
}
}
}
pub struct OpCStruct {
title: String,
another_value: String,
output_vale: i32,
}
impl OpCStruct {
pub fn new_OpC(in_title: String, in_another_value: String, in_output_vale: i32) -> OpCStruct {
OpCStruct {
title: in_title,
another_value: in_another_value,
output_vale: in_output_vale,
}
}
}
impl A {
pub fn new_A(in_name: String, in_operator: Op) -> A {
A {
name: in_name,
operator: in_operator,
}
}
}
pub enum Op {
OpB(OpBStruct),
OpC(OpCStruct),
}
pub struct A {
name: String,
operator: Op,
}
impl A {
pub fn new_A(in_name: String, in_operator: Op) -> A {
A {
name: in_name,
operator: in_operator,
}
}
}
The exact structure of OpBStruct and OpCStruct are arbitrary and could be anything.
How do I make sure OpBStruct and OpCStruct implement a certain trait?
trait OpTrait {
pub fn get_op_output(&self) -> i32;
}
I thought about making a sort of constructor function that checked for an OpTrait trait requirement and it would be the only way one could create an Op instance, but each operator requires different initialization parameters and there's no way to specify a variable number of inputs for a function in Rust.
Something like this doesn't work because there's no way to input the initialization parameters:
pub fn new_op<T: OpTrait>(operator: T) {
// --snip--
}
I thought about somehow using the new_A method implemented on A to check if the in_operator has implemented the trait, but I'm not sure how to do that either.
What is the correct pattern for this? If there is none, I can just implement the trait for each Op with no sort of interface around it.
I would also recommend writing a test, however you can write a function which is generic over a type but takes no arguments:
struct X {}
trait Y {
fn yo();
}
fn is_y<T: Y>(){}
Then you can add the following line to do the check
is_y::<X>();
which will compile only if X implements Y.
Using a unit test would be a fairly straightforward way to enforce that you want a given trait on a struct. You can do it via implicit test code, but a small utility function to do so can express the intent a little more clearly.
If you have indicated trait inputs on functions in the rest of the code, it might come out fairly naturally without the unit test. The test has the advantage of letting you have an opportunity to explicitly check some invariant of the trait implementation too.
struct A {
val: u8,
}
struct B {
val: u32,
}
trait ExpandToU64 {
fn to_u64(&self) -> u64;
}
impl ExpandToU64 for A {
fn to_u64(&self) -> u64
{
self.val as u64
}
}
fn trait_tester<E>(a: E)
where E: ExpandToU64
{
// the utility function doesn't have to even use the trait...
// but you probably want to exercise the logic a bit
//let v = a.to_u64();
let v = 24u64;
println!("{:?}", v);
}
#[test]
fn test_needs_trait_ExpandToU64() {
let a = A { val:1 };
trait_tester(a);
let b = B { val:2 };
trait_tester(b);
// This fails with a compile error
// "the trait `ExpandToU64` is not implemented for `B`"
}

Duplicate trait method implementation in Rust [duplicate]

I have two structs and a trait:
struct A {
x: u32,
}
struct B {
x: u32,
}
trait T {
fn double(&self) -> u32;
}
I would like to implement T for both structs using x.
Is there a way to write something like
impl T for A, B {
fn double(&self) -> u32 {
/* ... */
}
}
I would like to not use macros if possible.
The only way to implement a trait once for many concrete types is to implement a trait for all types already implementing another trait.
For example, you can implement a marker trait Xed and then:
impl<T> Double for T
where
T: Xed,
{
fn double(&self) {
/* ... */
}
}
However, Rust has principled generics. The only thing that you know about T in the previous implementation is that T implements the Xed trait, and therefore the only associated types/functions you can use are those coming from Xed.
A trait cannot expose a field/attribute, only associated types, constants and functions, so Xed would need a getter for x (which need not be called x).
If you wish to rely on syntactic (and not semantic) properties of the code, then use macros.
Creating a macro also solves your problem:
struct A {
x: u32,
}
struct B {
x: u32,
}
trait T {
fn double(&self) -> u32;
}
macro_rules! impl_T {
(for $($t:ty),+) => {
$(impl T for $t {
fn double(&self) -> u32 {
self.x * 2
}
})*
}
}
impl_T!(for A, B);
fn main() {}
Using the duplicate_item attribute macro you can do the following:
use duplicate::duplicate_item;
#[duplicate_item(name; [A]; [B])]
impl T for name {
fn double(&self) -> u32 {
self.x * 2
}
}
This will expand to two identical implementations for the two structs.
I know you said you didn't want to use macros, but I interpret that as meaning you don't want to roll your own macro, so I think this is a good compromise.
You could also use duplicate_item to avoid repeating your struct definitions:
use duplicate::duplicate_item;
#[duplicate_item(name; [A]; [B])]
struct name {
x: u32,
}
Or go all-out if you for some reason need two identical structs with identical implements (at this point we should begin questioning why we need 2 structs at all :D):
use duplicate::duplicate;
duplicate!{
[ name; [A]; [B] ]
pub struct name {
x: u32,
}
impl T for name {
fn double(&self) -> u32 {
self.x * 2
}
}
}
Notice the use of the duplicate function-like macro this time to duplicate struct and implement at the same time.
Since the internals of your structs are the same / share common components, you should extract them into a common struct and embed the common part back into the parent structs. The common struct would have the "complicated" implementation of the trait and then the parent struct's trait implementations would delegate to the common implementation:
trait T {
fn double(&self) -> u32;
}
struct A {
common: Common,
}
impl T for A {
fn double(&self) -> u32 {
self.common.double()
}
}
struct B {
common: Common,
}
impl T for B {
fn double(&self) -> u32 {
self.common.double()
}
}
struct Common {
x: u32,
}
impl T for Common {
fn double(&self) -> u32 {
self.x * 2
}
}
Any nicer code will require changes to the language. Two possible paths:
Fields in traits
Delegation

How to share functionality in Rust?

struct Vector {
data: [f32; 2]
}
impl Vector {
//many methods
}
Now I want to create a Normal which will almost behave exactly like a Vector but I need to differentiate the type. Because for example transforming a normal is different than transforming a vector. You need to transform it with the tranposed(inverse) matrix for example.
Now I could do it like this:
struct Normal {
v: Vector
}
And then reimplement all the functionality
impl Normal {
fn dot(self, other: Normal) -> f32 {
Vector::dot(self.v, other.v)
}
....
}
I think I could also do it with PhantomData
struct VectorType;
struct NormalType;
struct PointType;
struct Vector<T = VectorType> {
data: [f32; 2],
_type: PhantomData<T>,
}
type Normal = Vector<NormalType>;
But then I also need a way to implement functionality for specific types.
It should be easy to implement for example add for everything so that it is possible to add point + vector.
Or functionality specific to some type
impl Vector<NormalType> {..} // Normal specific stuff
Not sure how I would implement functionality for a subrange. For example maybe the dot product only makes sense for normals and vectors but not points.
Is it possible to express boolean expression for trait bounds?
trait NormalTrait;
trait VectorTrait;
impl NormalTrait for NormalType {}
impl VectorTrait for VectorType {}
impl<T: PointTrait or VectorTrait> for Vector<T> {
fn dot(self, other: Vector<T>) -> f32 {..}
}
What are my alternatives?
Your question is pretty broad and touches many topics. But your PhantomData idea could be a good solution. You can write different impl blocks for different generic types. I added a few things to your code:
struct VectorType;
struct NormalType;
struct PointType;
struct Vector<T = VectorType> {
data: [f32; 2],
_type: PhantomData<T>,
}
type Normal = Vector<NormalType>;
type Point = Vector<PointType>;
// --- above this line is old code --------------------
trait Pointy {}
impl Pointy for VectorType {}
impl Pointy for PointType {}
// implement for all vector types
impl<T> Vector<T> {
fn new() -> Self {
Vector {
data: [0.0, 0.0],
_type: PhantomData,
}
}
}
// impl for 'pointy' vector types
impl<T: Pointy> Vector<T> {
fn add<R>(&mut self, other: Vector<R>) {}
fn transform(&mut self) { /* standard matrix multiplication */ }
}
// impl for normals
impl Vector<NormalType> {
fn transform(&mut self) { /* tranposed inversed matrix stuff */ }
}
fn main() {
let mut n = Normal::new();
let mut p = Point::new();
n.transform();
p.transform();
// n.add(Normal::new()); // ERROR:
// no method named `add` found for type `Vector<NormalType>` in the current scope
p.add(Point::new());
}
Is it possible to express boolean expression for trait bounds?
No (not yet). But you can solve it in this case as shown above: you create a new trait (Pointy) and implement it for the types in your "or"-condition. Then you bound with that trait.

How do I create a heterogeneous collection of objects?

I want to use trait objects in a Vec. In C++ I could make a base class Thing from which is derived Monster1 and Monster2. I could then create a std::vector<Thing*>. Thing objects must store some data e.g. x : int, y : int, but derived classes need to add more data.
Currently I have something like
struct Level {
// some stuff here
pub things: Vec<Box<ThingTrait + 'static>>,
}
struct ThingRecord {
x: i32,
y: i32,
}
struct Monster1 {
thing_record: ThingRecord,
num_arrows: i32,
}
struct Monster2 {
thing_record: ThingRecord,
num_fireballs: i32,
}
I define a ThingTrait with methods for get_thing_record(), attack(), make_noise() etc. and implement them for Monster1 and Monster2.
Trait objects
The most extensible way to implement a heterogeneous collection (in this case a vector) of objects is exactly what you have:
Vec<Box<dyn ThingTrait + 'static>>
Although there are times where you might want a lifetime that's not 'static, so you'd need something like:
Vec<Box<dyn ThingTrait + 'a>>
You could also have a collection of references to traits, instead of boxed traits:
Vec<&dyn ThingTrait>
An example:
trait ThingTrait {
fn attack(&self);
}
impl ThingTrait for Monster1 {
fn attack(&self) {
println!("monster 1 attacks")
}
}
impl ThingTrait for Monster2 {
fn attack(&self) {
println!("monster 2 attacks")
}
}
fn main() {
let m1 = Monster1 {
thing_record: ThingRecord { x: 42, y: 32 },
num_arrows: 2,
};
let m2 = Monster2 {
thing_record: ThingRecord { x: 42, y: 32 },
num_fireballs: 65,
};
let things: Vec<Box<dyn ThingTrait>> = vec![Box::new(m1), Box::new(m2)];
}
Box<dyn SomeTrait>, Rc<dyn SomeTrait>, &dyn SomeTrait, etc. are all trait objects. These allow implementation of the trait on an infinite number of types, but the tradeoff is that it requires some amount of indirection and dynamic dispatch.
See also:
What makes something a "trait object"?
What does "dyn" mean in a type?
Enums
As mentioned in the comments, if you have a fixed number of known alternatives, a less open-ended solution is to use an enum. This doesn't require that the values be Boxed, but it will still have a small amount of dynamic dispatch to decide which concrete enum variant is present at runtime:
enum Monster {
One(Monster1),
Two(Monster2),
}
impl Monster {
fn attack(&self) {
match *self {
Monster::One(_) => println!("monster 1 attacks"),
Monster::Two(_) => println!("monster 2 attacks"),
}
}
}
fn main() {
let m1 = Monster1 {
thing_record: ThingRecord { x: 42, y: 32 },
num_arrows: 2,
};
let m2 = Monster2 {
thing_record: ThingRecord { x: 42, y: 32 },
num_fireballs: 65,
};
let things = vec![Monster::One(m1), Monster::Two(m2)];
}
See also:
Why does an enum require extra memory size?

Resources