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.
Related
I am new to the composition approach with Rust, and I am having a hard time trying to figure out whether I could make my code more efficient / smaller.
Let us assume that I have a base struct BaseStruct:
struct BaseStruct {
values: Vec<i32>,
}
impl BaseStruct {
fn new() -> Self {
Self{values: vec![]}
}
}
Then, I define a AsBase trait to ease the composition process:
/ Trait used to do composition
trait AsBase {
fn as_base(&mut self) -> &mut BaseStruct;
// Note that add_val has a default implementation!
fn add_val(&mut self, val: i32) {
// let us assume that this method has hundreds of lines of code
self.as_base().values.push(val);
}
}
// Note that BaseStruct implements the AsBase trait!
impl AsBase for BaseStruct {
fn as_base(&mut self) -> &mut BaseStruct {
self
}
}
Note that BaseStruct implements the AsBase trait! Otherwise, we couldn't add a value to the vector.
Then, I derive the behavior of the base struct using composition:
// Derived struct and handy functions
struct DerivedStruct {
base: BaseStruct,
}
impl DerivedStruct {
fn new() -> Self {
Self{base: BaseStruct::new()}
}
}
// Derived struct also implements the AsBase trait
impl AsBase for DerivedStruct {
fn as_base(&mut self) -> &mut BaseStruct {
&mut self.base
}
}
So now, I can add values to the inner vector of my derived struct using the trait method!
fn main() {
let mut base = BaseStruct::new();
base.add_val(1);
let mut derived = DerivedStruct::new();
derived.add_val(1); // With composition and AsBase trait, I achieve "inheritance"
}
Here you have a playground with this example.
However, what if the add_val default method was very complex and required hundreds of lines of code? Would Rust generate a different method add_val for every struct implementing the AsBase trait? Or is the compiler smart enough to detect that they can share the same function?
Let me try to be clearer: would this alternative implementation be smaller is size, as it explicitly uses the same method?
// Base struct and handy associated methods
struct BaseStruct {
values: Vec<i32>,
}
impl BaseStruct {
fn new() -> Self {
Self{values: vec![]}
}
fn add_val(&mut self, val: i32) {
// Let us assume that this method is hundreds of lines long
self.values.push(val);
}
}
// Trait used to do composition
trait AsBase {
fn as_base(&mut self) -> &mut BaseStruct;
// Note that add_val has a default implementation!
fn add_val(&mut self, val: i32) {
self.as_base().add_val(val);
}
}
// Note that BaseStruct does NOT implement the AsBase trait to avoid naming collision!
// Derived struct and handy functions
struct DerivedStruct {
base: BaseStruct,
}
impl DerivedStruct {
fn new() -> Self {
Self{base: BaseStruct::new()}
}
}
// Derived struct also implements the AsBase trait
impl AsBase for DerivedStruct {
fn as_base(&mut self) -> &mut BaseStruct {
&mut self.base
}
}
fn main() {
let mut base = BaseStruct::new();
base.add_val(1);
let mut derived = DerivedStruct::new();
derived.add_val(1); // With composition and AsBase trait, I achieve "inheritance"
}
(Also, note that I couldn't implement the AsBase trait for BaseStruct due to naming collisions, I don't know if there is a workaround to avoid this other than changing the names of the methods).
Here you have a playground for the alternative version.
Yes, the compiler will generate a new instance of add_val() for each type. It may collapse them if they use the same machine code, but it will not if they don't. If you want to avoid that, a common way is to define a nested function (see Why does std::fs::write(...) use an inner function?):
fn add_val(&mut self, val: i32) {
fn inner(this: &mut BaseStruct) {
// let us assume that this method has hundreds of lines of code
base.values.push(val);
}
inner(self.as_base());
}
However, what you're doing is not composition. Rather, it's emulating inheritance with composition. When using the composition principle, you should not (usually) have an AsBase trait because you should not treat DerivedStruct as BaseStruct. This is not "is-a" relationship, this is a "has-a" relationship. If a method needs a BaseStruct, pass it a reference to the field directly and let it perform its work.
I'm working on an implementation of polynomials which uses a C library in the case of integer coefficients. However, I want to define a different implementation when coefficients come from some other ring. When we will be using the C library we need to handle some underlying values which we pass to C, grouped in a struct. Otherwise, there is no need for these values to be defined. How can I implement this? Here is a mock up of what I want:
pub struct Poly<T> {
coeff_type: T,
c_value: StructDependingOnT, // only needs to be defined when T is an integer for example
}
My thought was to have a trait specifying when a coefficient type means we will be using the C library:
pub struct Poly<T> {
coeff_type: T,
}
pub trait UsesC<T> { // T is the underlying c_value needed above
fn get_c_value(&self) -> T;
}
impl UsesC<StructDependingOnT> for Poly<CoefficientType> {
fn get_c_value(&self) -> StructDependingOnT {
// ??
}
}
The issue here is c_value is not a field of the struct. Is there a way to have a field defined only sometimes, like when it implements a certain trait? Defining an associated constant for UsesC is close to what I want, but it would need to be mutable.
You can't cause the field to disappear, but you can use a zero-sized type.
It requires a bit of trickery using a new trait with an associated type for each T you want to support.
fn main() {
let p1: Poly<f32> = Poly::default();
let p2: Poly<i32> = Poly::default();
println!("p1 = {:?}", p1); // "p1 = Poly { coeff_type: 0.0, c_value: () }"
println!("p2 = {:?}", p2); // "p2 = Poly { coeff_type: 0, c_value: IntRing }"
}
use core::fmt::Debug;
pub trait Polyable {
type Extra: Default + Debug;
}
#[derive(Default, Debug)]
pub struct Poly<T: Polyable> {
coeff_type: T,
c_value: <T as Polyable>::Extra,
}
#[derive(Default, Debug)]
pub struct IntRing {}
impl Polyable for i32 {
type Extra = IntRing;
}
impl Polyable for f32 {
type Extra = ();
}
In Rust, is it possible to generalize a trait to always implement another trait, or to have a struct "inherit" its traits' implementations of other traits?
For instance, in the following code, I don't want to have to implement equality between every possible type of Length. If I implement it on the trait, how do I use it on the structs that implement that trait?
// `Length` Trait, which includes a standardized unit
trait Length {
fn _standardized(&self) -> f64;
}
impl std::cmp::PartialEq for dyn Length {
fn eq(&self, other: &Self) -> bool {
self._standardized() == other._standardized()
}
}
// `Centimeters`
struct Centimeters(f64);
impl Length for Centimeters {
fn _standardized(&self) -> f64 {
let &Centimeters(cm) = self;
cm
}
}
// `Inches`
struct Inches(i32);
impl Length for Inches {
fn _standardized(&self) -> f64 {
let &Inches(inches) = self;
inches as f64 * 2.54
}
}
fn main() {
let foot = Inches(12);
let meter = Centimeters(100.0);
let cmp = if foot == meter { "equal" } else { "not equal" };
println!("One foot is {} to one meter.", cmp);
}
Currently, that throws an error:
binary operation `==` cannot be applied to type `partial_ord::Centimeters`
note: an implementation of `std::cmp::PartialEq` might be missing for `partial_ord::Centimeters`
rustc(E0369)
Based on this question, I also tried:
impl<T> std::cmp::PartialEq for T where T: Length {
which gives
conflicting implementations of trait `std::cmp::PartialEq<&_>` for type `&_`:
Is there a way to do what I'm trying to do? If so, what is it?
(And, if you're looking for extra kudos, what gap in my Rust paradigm does this example highlight?)
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
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