Sharing a struct with trait objects as properties across threads - rust

I have the code below. With the commented out parts, it's working. When I uncomment the parts it does not compile anymore.
How can I adjust the commented parts to make them work, i.e., I want to make threads access the expression tree simultaneously.
When I try it, the compiler starts with errors about thread safeness.
I read the Rust book and know C/C++, but didn't understood everything about Rust type system and semantics yet.
use std::thread;
use std::sync::Arc;
pub trait Expr {
fn run(&self) -> i32;
}
pub struct ConstantExpr {
n: i32,
}
impl ConstantExpr {
pub fn new(n: i32) -> Self {
Self { n }
}
}
impl Expr for ConstantExpr {
fn run(&self) -> i32 {
self.n
}
}
pub struct AddExpr {
expr1: Box<Expr>,
expr2: Box<Expr>,
}
impl AddExpr {
pub fn new(expr1: Box<Expr>, expr2: Box<Expr>) -> Self {
Self { expr1, expr2 }
}
}
impl Expr for AddExpr {
fn run(&self) -> i32 {
self.expr1.run() + self.expr2.run()
}
}
struct Container {
x: i32,
cached_expr: Arc<Expr>,
}
impl Container {
fn new() -> Self {
Self {
x: 0,
cached_expr: Arc::new(AddExpr::new(
Box::new(ConstantExpr::new(10)),
Box::new(ConstantExpr::new(1)),
)),
}
}
}
fn main() {
let container = Arc::new(Container::new());
let container1 = Arc::clone(&container);
/*
let thread1 = thread::spawn(move || {
println!("thread1: {}", container1.x);
println!("thread1: {}", container1.cached_expr.run());
});
*/
println!("main: {}", container.x);
println!("main: {}", container.cached_expr.run());
//thread1.join().unwrap();
}
The error:
error[E0277]: the trait bound `Expr + 'static: std::marker::Send` is not satisfied
--> src/main.rs:64:19
|
64 | let thread1 = thread::spawn(move || {
| ^^^^^^^^^^^^^ `Expr + 'static` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `Expr + 'static`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<Expr + 'static>`
= note: required because it appears within the type `Container`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<Container>`
= note: required because it appears within the type `[closure#src/main.rs:64:33: 67:6 container1:std::sync::Arc<Container>]`
= note: required by `std::thread::spawn`
error[E0277]: the trait bound `Expr + 'static: std::marker::Sync` is not satisfied
--> src/main.rs:64:19
|
64 | let thread1 = thread::spawn(move || {
| ^^^^^^^^^^^^^ `Expr + 'static` cannot be shared between threads safely
|
= help: the trait `std::marker::Sync` is not implemented for `Expr + 'static`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<Expr + 'static>`
= note: required because it appears within the type `Container`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<Container>`
= note: required because it appears within the type `[closure#src/main.rs:64:33: 67:6 container1:std::sync::Arc<Container>]`
= note: required by `std::thread::spawn`

I find the error message pretty straightforward:
the trait std::marker::Send is not implemented for Expr + 'static
required because of the requirements on the impl of std::marker::Send for std::sync::Arc<Expr + 'static>
required because it appears within the type Container
required because of the requirements on the impl of std::marker::Send for std::sync::Arc<Container>
required because it appears within the type [closure#src/main.rs:64:33: 67:6 container1:std::sync::Arc<Container>]
required by std::thread::spawn
You are trying to move your Arc<Container> to another thread, but it contains an Arc<Expr + 'static>, which cannot be guaranteed to be safely sent (Send) or shared (Sync) across threads.
Either add Send and Sync as supertraits to Expr:
pub trait Expr: Send + Sync { /* ... */ }
Or add them as trait bounds to your trait objects:
pub struct AddExpr {
expr1: Box<Expr + Send + Sync>,
expr2: Box<Expr + Send + Sync>,
}
impl AddExpr {
pub fn new(expr1: Box<Expr + Send + Sync>, expr2: Box<Expr + Send + Sync>) -> Self {
Self { expr1, expr2 }
}
}
struct Container {
x: i32,
cached_expr: Arc<Expr + Send + Sync>,
}
See also:
How can I share references across threads?
Multithreaded application fails to compile with error-chain
Is there any way to implement the Send trait for ZipFile?
How do I share a generic struct between threads using Arc<Mutex<MyStruct<T>>>?

Related

HashMap <Foo,Fn(Foo)> size for values of type `(dyn Fn(Foo) + 'static)` cannot be known

I'm trying to create a sort-of observer pattern in rust and I am getting a compile error that is basically telling me (I think) it doesn't know the size of a function param at runtime. I didn't realize the size of a function pointer was unknown at runtime. More importantly, I don't have a clue how to tell it the size
christianb#christianb-mac debug % cargo build
Compiling pyrsia-blockchain v0.1.0 (/Users/christianb/dev/jfrog/rusty-brown)
error[E0277]: the size for values of type `(dyn Fn(Foo) -> Result<(), (dyn std::error::Error + 'static)> + 'static)` cannot be known at compilation time
--> src/main.rs:45:16
|
45 | observers: HashMap<&'a Foo, OnFooDone>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Fn(Foo) -> Result<(), (dyn std::error::Error + 'static)> + 'static)`
note: required by a bound in `HashMap`
error[E0277]: the size for values of type `(dyn std::error::Error + 'static)` cannot be known at compilation time
--> src/main.rs:45:16
|
45 | observers: HashMap<&'a Foo, OnFooDone>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn std::error::Error + 'static)`
note: required by a bound in `Result`
error[E0277]: the size for values of type `(dyn std::error::Error + 'static)` cannot be known at compilation time
--> src/main.rs:49:54
|
49 | pub fn submit_foo(&mut self, foo: &Foo, on_done: OnFooDone) -> &Self {
| ^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn std::error::Error + 'static)`
note: required by a bound in `Result`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `pyrsia-blockchain` due to 3 previous errors
use std::collections::HashMap;
use std::error::Error;
fn main() {
let mut holder = Holder {
observers: HashMap::new()
};
let foo0 = Foo {
id: 0,
stuff: **"hello",
};
let foo1 = Foo {
id: 1,
stuff: **"world",
};
let mut foo2 = Foo {
id: 2,
stuff: **"bob",
};
let mut magic_num = 5;
let mut closure = |foo| {
println!("Slow");
magic_num += foo.id;
foo.id
};
holder.submit_foo(&foo0, |f| {
println!("received foo {}", f.id)?
});
holder.submit_foo(&foo1, |f| {
println!("received foo2 {}", f.id)?
});
holder.submit_foo(&foo2, closure);
holder.notify_all();
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Foo {
id: u64,
stuff: str,
}
type OnFooDone = dyn Fn(Foo) -> Result<(),dyn Error>;
pub struct Holder<'a> {
observers: HashMap<&'a Foo, OnFooDone>,
}
impl Holder<'_> {
pub fn submit_foo(&mut self, foo: &Foo, on_done: OnFooDone) -> &Self {
self.observers.insert(foo, on_done);
self
}
pub fn notify_all(self) -> Self {
self.observers.iter().for_each(|k, f| f(k));
self
}
}
The problem is with:
type OnFooDone = dyn Fn(Foo) -> Result<(),dyn Error>;
This is a so-called unsized type because its size is not known at compile time. It's not a function pointer.
Let's start with the type itself. Imagine if you have just a regular function, but also a closure. It's pretty obvious now, that the closure is not the same size as the function pointer, but both implement Fn.
The same issue is present with the Result. What if the function returns several different types that implement dyn Error ?
The solution is to either use references or box the dynamically sized types:
You can return a boxed error, such as Box<dyn Error>> instead of just dyn Error
You can either box the Fn, just like we did with error above, or store a reference: HashMap<&'a Foo, &'a OnFooDone>
Thus we get:
type OnFooDone = dyn Fn(Foo) -> Result<(),Box<dyn Error>>;
pub struct Holder<'a> {
observers: HashMap<&'a Foo, &'a OnFooDone>,
// or with a boxed triat
observers: HashMap<&'a Foo, Box<OnFooDone>>,
}
Resources:
Returning closures

Storing FnMut in struct gives lifetime problems

I'm trying to store a FnMut in a struct:
struct OpenVPNSocket {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()>>>,
}
impl OpenVPNSocket {
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()>>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
I get this error:
error[E0310]: the parameter type `F` may not live long enough
--> src/lib.rs:8:42
|
7 | fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()>>(&mut self, callback: Box<F>) {
| -- help: consider adding an explicit lifetime bound...: `F: 'static +`
8 | self.socket_send_callback = Some(callback);
| ^^^^^^^^ ...so that the type `F` will meet its required lifetime bounds
I understand lifetime as something to do with references. However I don't use references. I don't see why my struct cannot store a Box. A Box lives as long as it's used.
UPDATE:
I have this example:
use std::sync::Arc;
pub type OnConsume = Arc<dyn Fn() -> Option<u8> + Send + Sync>;
struct Test {
callback: OnConsume
}
impl Test {
fn set_on_consume(&mut self, f: OnConsume) {
self.callback = f;
}
}
which works. What is the difference from the previous one?
In Rust, values also have lifetimes. Take, for example, this struct:
struct RefWrapper<'a> {
some_ref: &'a u32
}
An instance of RefWrapper is not a reference, but contains a lifetime. Since you're moving the box into the struct, which could live for the duration of the program (the method makes no guarantees as to when the struct instance could be dropped), the function must live for the maximum lifetime, the static lifetime.
All trait objects have lifetimes, the default implicit lifetime for boxed trait objects is 'static so your struct's socket_send_callback actually has an implicit + 'static bound. Shown in context:
struct OpenVPNSocket {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()> + 'static>>,
}
Since the boxed trait object has to be bounded by a 'static lifetime when you write a function to set this field the value itself must have a 'static lifetime which is why the compiler suggests adding that explicit bound. Fixed example with added bound:
impl OpenVPNSocket {
// notice the added 'static bound for F
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()> + 'static>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
With this change your code will compile. If you want to accept trait objects that aren't bounded by 'static lifetimes then you can do that by making your OpenVPNSocket generic over lifetimes. This alternative solution also compiles:
struct OpenVPNSocket<'a> {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()> + 'a>>,
}
impl<'a> OpenVPNSocket<'a> {
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()> + 'a>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
The reason why this code works is because you define the type once and use it in multiple places, and in all places it has the implicit 'static bound. Desugared:
use std::sync::Arc;
pub type OnConsume = Arc<dyn Fn() -> Option<u8> + Send + Sync + 'static>;
struct Test {
callback: OnConsume
}
impl Test {
fn set_on_consume(&mut self, f: OnConsume) {
self.callback = f;
}
}
However you can do the exact same thing in your prior code as well:
type Callback = Box<dyn FnMut(Vec<u8>) -> Result<(), ()>>;
struct OpenVPNSocket {
socket_send_callback: Option<Callback>,
}
impl OpenVPNSocket {
fn set_socket_send(&mut self, callback: Callback) {
self.socket_send_callback = Some(callback);
}
}
The above also compiles, and the implicit 'static bound is still there.

Implement From with multiple type parameters

I was playing around the visitor design pattern in Rust. My initial visitor didn't have any type parameter so I was able to do something like this:
impl<T> From<T> for Box<dyn VisitorElement>
where
T: VisitorElement + 'static,
{
fn from(elem: T) -> Box<dyn VisitorElement> {
Box::new(elem)
}
}
As my visitor was simply a f32 calculator, I wanted to make it more generic by replacing f32 by a type parameter. After passing the proper type parameter everywhere, my From implementation wouldn't work. Here's a complete example (playground):
/*
* Visitor traits
*/
trait Visitor<T> {
fn visit_literal(&mut self, literal: &Literal<T>);
}
trait VisitorElement<T> {
fn accept(&self, visitor: &mut dyn Visitor<T>);
}
/*
* Literal value
*/
struct Literal<T> {
value: T,
}
impl<T> Literal<T> {
fn new(value: T) -> Self {
Self { value: value }
}
}
impl<T> VisitorElement<T> for Literal<T> {
fn accept(&self, visitor: &mut dyn Visitor<T>) {
visitor.visit_literal(self)
}
}
impl<K, T> From<K> for Box<dyn VisitorElement<T>>
where
K: VisitorElement<T> + 'static,
{
fn from(elem: K) -> Box<dyn VisitorElement<T>> {
Box::new(elem)
}
}
fn main() {
let element = Literal::new(0.1);
let boxed: Box<dyn VisitorElement<_>> = element.into();
}
error[E0119]: conflicting implementations of trait `std::convert::From<std::boxed::Box<(dyn VisitorElement<_> + 'static)>>` for type `std::boxed::Box<(dyn VisitorElement<_> + 'static)>`:
--> src/main.rs:28:1
|
28 | / impl<K, T> From<K> for Box<dyn VisitorElement<T>>
29 | | where
30 | | K: VisitorElement<T> + 'static,
31 | | {
... |
34 | | }
35 | | }
| |_^
|
= note: conflicting implementation in crate `core`:
- impl<T> std::convert::From<T> for T;
= note: downstream crates may implement trait `VisitorElement<_>` for type `std::boxed::Box<(dyn VisitorElement<_> + 'static)>`
My goal was to allow auto boxing dynamic types in order to make code typing less verbose.
From How is there a conflicting implementation of `From` when using a generic type?, I can see that the type S can cause the trait to convert to itself but in my case the type K is constrained to VisitorElement but I stille end up with a conflict with a Box<dyn VisitorElement<_> + 'static>.
Is there a reason why the constraint on K: VisitorElement<T> + 'static allows a Box<dyn VisitorElement<T> + 'static> into itself but K: VisitorElement<f32> doesn't?
From my understanding, the type K can't be a Box because Box doesn't implement VisitorElement<T>, so I should never end up with a Box as K.
If the issue was caused by the Into being automatically implemented... I'd have From> for Literal<_> then Literal<_> into Box<dyn VisitorElement<_>, but that's true for any type. In my case it doesn't explicitly do that, unless the Box is a special type that implements the same trait as its content?

Cannot define appropriate rust lifetime requirements

TL;DR
Having problems with lifetime with nested objects. Code is below.
Long Version:
I'm writing a multiplayer game using ggez, and I'm trying to create an abstraction layer for the input (to allow local and remote players play together).
In order to so, I've created an Input trait, and implemented KeyboardInput for local input, which uses ggez keyboard state querying methods.
Now the tricky part: ggez creates Context object at startup, and expects a reference to it in most of the functions that is exposes.
because my KeyboardInput implementation is using ggez input method (specifically, is_key_pressed), it has to pass &Context to this method. However, since the trait itself should be generic, it won't need a Context reference for any other implementation (for example, NetworkInput).
my solution was to add a reference to Context as field in the KeyboardInput struct. However, that caused a lifetime error that I'm still unable to resolve.
I also tried to make the lifetime 'static, but that did not work either.
here is the relevant code:
pub trait Input {
fn get_direction(&self) -> Direction;
}
pub struct KeyboardInput<'a> {
left_key: KeyCode,
right_key: KeyCode,
_ctx: &'a Context
}
impl KeyboardInput<'_> {
pub fn new(_ctx: &Context, left_key: KeyCode, right_key: KeyCode) -> KeyboardInput {
KeyboardInput{
_ctx,
left_key,
right_key
}
}
}
impl Input for KeyboardInput<'_> {
fn get_direction(&self) -> Direction {
if ggez::input::keyboard::is_key_pressed(self._ctx, self.left_key) {
return Direction::Left;
}
if ggez::input::keyboard::is_key_pressed(self._ctx, self.right_key) {
return Direction::Right;
}
Direction::Unchanged
}
}
struct Player {
angle: f32,
pos_x: f32,
pos_y: f32,
input_manager: Box<dyn Input>,
}
impl <'a>MainState {
fn new(ctx: &'a Context) -> GameResult<MainState> {
let kbd_input = KeyboardInput::new(ctx, KeyCode::Left, KeyCode::Right);
let kbd_input = Box::new(kbd_input);
let s = MainState {
last_update: Instant::now(),
players: vec![
Player::new(kbd_input)
]
};
Ok(s)
}
}
pub fn main() -> GameResult {
let cb = ggez::ContextBuilder::new("My game", "ggez");
let (ctx, event_loop) = &mut cb.build()?;
let state = &mut MainState::new(&ctx)?;
event::run(ctx, event_loop, state)
}
and the compiler error:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src\main.rs:75:25
|
75 | let kbd_input = KeyboardInput::new(ctx, KeyCode::Left, KeyCode::Right);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 72:7...
--> src\main.rs:72:7
|
72 | impl <'a>MainState {
| ^^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:75:44
|
75 | let kbd_input = KeyboardInput::new(ctx, KeyCode::Left, KeyCode::Right);
| ^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn input_manager::Input + 'static)>
found std::boxed::Box<dyn input_manager::Input>
This error almost always means that you are trying to store a value in a way where it can exist longer than it's lifetime.
Let's look at a specific piece of code, annotated with explicit types and lifetimes:
impl<'a> MainState {
fn new(ctx: &'a Context) -> GameResult<MainState> {
let kbd_input: KeyboardInput<'a> = KeyboardInput::new(ctx, KeyCode::Left, KeyCode::Right);
let kbd_input: Box<dyn Input + 'a> = Box::new(kbd_input);
let s: MainState = MainState {
last_update: Instant::now(),
players: vec![
Player::new(kbd_input as Box<dyn Input + 'static>)
]
};
Ok(s)
}
}
On line 9, you're trying to assign kbd_input to Box<dyn Input>. But Box<dyn Input> has no explicit lifetime, it is implicitly equivalent to Box<dyn Input + 'static>. So you're trying to assign a value with lifetime 'a to a type with a static lifetime, which is not allowed.
The solution is to explicitly set the lifetime of the trait object type: Box<dyn Input + 'a>. This cascades and means that you'll need to add a lifetime to the MainState struct as well, as it now will contain a type with a non-static lifetime:
struct MainState<'a> {
/* ... */
players: Vec<Box<dyn Input + 'a>>,
}

Is there a way to implement a trait on top of another trait? [duplicate]

This question already has answers here:
I implemented a trait for another trait but cannot call methods from both traits
(3 answers)
Closed 5 years ago.
I am trying to create a base trait that will implement other operator traits (Add, Subtract, Multiply, Divide, etc...) for me.
This fails to compile, it looks like an issued with Sized, but even when Measurement is set to require Sized it does not work. Is this even possible?
use std::ops::Add;
#[derive(Copy, Clone, Debug)]
struct Unit {
value: f64,
}
impl Unit {
fn new(value: f64) -> Unit {
Unit { value: value }
}
}
trait Measurement: Sized {
fn get_value(&self) -> f64;
fn from_value(value: f64) -> Self;
}
impl Measurement for Unit {
fn get_value(&self) -> f64 {
self.value
}
fn from_value(value: f64) -> Self {
Unit::new(value)
}
}
// This explicit implementation works
/*
impl Add for Unit {
type Output = Unit;
fn add(self, rhs: Unit) -> Unit {
let a = self.get_value();
let b = rhs.get_value();
Unit::from_value(a + b)
}
}
*/
// This trait implementation does not
impl Add for Measurement {
type Output = Self;
fn add(self, rhs: Self) -> Self {
let a = self.get_value();
let b = rhs.get_value();
Self::from_value(a + b)
}
}
fn main() {
let a = Unit::new(1.5);
let b = Unit::new(2.0);
let c = a + b;
println!("{}", c.get_value());
}
(playground)
error[E0277]: the trait bound `Measurement + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:42:6
|
42 | impl Add for Measurement {
| ^^^ `Measurement + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Measurement + 'static`
error[E0038]: the trait `Measurement` cannot be made into an object
--> src/main.rs:42:6
|
42 | impl Add for Measurement {
| ^^^ the trait `Measurement` cannot be made into an object
|
= note: the trait cannot require that `Self : Sized`
error[E0038]: the trait `Measurement` cannot be made into an object
--> src/main.rs:43:5
|
43 | type Output = Self;
| ^^^^^^^^^^^^^^^^^^^ the trait `Measurement` cannot be made into an object
|
= note: the trait cannot require that `Self : Sized`
error[E0038]: the trait `Measurement` cannot be made into an object
--> src/main.rs:45:5
|
45 | fn add(self, rhs: Self) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Measurement` cannot be made into an object
|
= note: the trait cannot require that `Self : Sized`
The issue is not with Sized. The syntax you're looking for is:
impl<T: Measurement> Add for T { ... }
instead of:
impl Add for Measurement { ... }
Because the right-hand side of the for must be an object, not a trait, however a type parameter constrained to a trait (i.e. a T required to be Measurement) is valid.
Now your code still won't compile. You will get the following:
error: type parameter T must be used as the type parameter for some
local type (e.g. MyStruct<T>); only traits defined in the current
crate can be implemented for a type parameter [E0210]
The issue here is of a totally different kind. I'm not sure it's related to the question any more but I'll still explain what's going on. When you write an impl for Add to any T which is Measurement, you open the possibility that a type would already implement Add on its own, and would also implement Measurement elsewhere. Imagine if you wanted to implement Measurement on u8 (which is silly but possible): which impl should Rust choose for Add? The original std impl or your Measurement impl? (in-depth discussion about this issue)
Right now Rust plainly forbids an impl if it is not at least 1) your own trait or 2) your own type (where "own" formally means, in the crate you're writing your impl). This is why you can write impl Add for Unit: because you own Unit.
The easiest solution would be to give up and implement Add independently for each type you're planning to make Unit. Say your crate defines Inches and Centimeter, each one would have its own Add impl. If the code is insultingly similar, and you feel you broke DRY, leverage macros. Here is how the std crate does it:
macro_rules! add_impl {
($($t:ty)*) => ($(
#[stable(feature = "rust1", since = "1.0.0")]
impl Add for $t {
type Output = $t;
#[inline]
fn add(self, other: $t) -> $t { self + other }
}
forward_ref_binop! { impl Add, add for $t, $t }
)*)
}
You cannot implement a trait for a trait, you implement a trait only for types.
But you can implement a trait for a generic type that implement a certain traits (trait bounds).
Something like this:
impl<T : Measurement> Add<T> for T {
type Output = T;
fn add(self, rhs: Self) -> T {
let a = self.get_value();
let b = rhs.get_value();
T::from_value(a + b)
}
}
Unfortunately you can do this only for traits defined in your crate (its called coherence), so you cannot do that for the std Add trait because it's defined in the std crate, not in yours.
I think you might need to define some macros to do what you want to do.
Here's a working version with macros, as suggested:
use std::ops::Add;
#[derive(Copy, Clone, Debug)]
struct Unit {
value: f64,
}
impl Unit {
fn new(value: f64) -> Unit {
Unit { value: value }
}
}
trait Measurement: Sized {
fn get_value(&self) -> f64;
fn from_value(value: f64) -> Self;
}
impl Measurement for Unit {
fn get_value(&self) -> f64 {
self.value
}
fn from_value(value: f64) -> Self {
Unit::new(value)
}
}
macro_rules! add_impl {
($($t:ty)*) => ($(
impl Add for $t {
type Output = $t;
fn add(self, other: $t) -> $t {
let a = self.get_value();
let b = other.get_value();
let r = a + b;
Self::from_value(r)
}
}
)*)
}
add_impl! { Unit }
fn main() {
let a = Unit::new(1.5);
let b = Unit::new(2.0);
let c = a + b;
println!("{}", c.get_value());
}

Resources