Force/coerce evaluation of closure signature - rust

Here is a contrived example of what I am trying to do:
use std::boxed::Box;
#[derive(Debug)]
pub struct Foo<'a>(pub &'a str);
pub trait IntoBox {
fn into_box<'a>(self) -> Box<Fn(Foo) -> String>;
}
impl<B> IntoBox for B where B: Fn(Foo) -> String + 'static {
fn into_box(self) -> Box<Fn(Foo) -> String> { Box::new(self) }
}
fn direct_into_box<B: Fn(Foo) -> String + 'static>(b: B) -> Box<Fn(Foo) -> String> {
Box::new(b)
}
fn main() {
// Doesn't work
let x = IntoBox::into_box(|i| format!("{:?}", i) );
// Works
let y = IntoBox::into_box(|i: Foo| format!("{:?}", i) );
// Also works
let z = direct_into_box(|i| format!("{:?}", i) );
}
How do I get my trait impl to do the same evaluation of the closure as is done by my direct_into_box? I would have expected direct_into_box and my trait impl to behave in the same way.
The error on x:
error[E0271]: type mismatch resolving `for<'r> <[closure#<anon>:20:31: 20:53] as std::ops::FnOnce<(Foo<'r>,)>>::Output == std::string::String`
--> <anon>:20:13
|
20 | let x = IntoBox::into_box(|i| format!("{:?}", i) );
| ^^^^^^^^^^^^^^^^^ expected bound lifetime parameter , found concrete lifetime
|
= note: concrete lifetime that was found is lifetime '_#29r
= note: required because of the requirements on the impl of `IntoBox` for `[closure#<anon>:20:31: 20:53]`
= note: required by `IntoBox::into_box`
error[E0281]: type mismatch: the type `[closure#<anon>:20:31: 20:53]` implements the trait `std::ops::Fn<(_,)>`, but the trait `for<'r> std::ops::Fn<(Foo<'r>,)>` is required (expected concrete lifetime, found bound lifetime parameter )
--> <anon>:20:13
|
20 | let x = IntoBox::into_box(|i| format!("{:?}", i) );
| ^^^^^^^^^^^^^^^^^
|
= note: required because of the requirements on the impl of `IntoBox` for `[closure#<anon>:20:31: 20:53]`
= note: required by `IntoBox::into_box`

Sounds like an inference bug in the compiler. What seems to happen is that the compiler implements Fn(Foo<'x>) for one specific lifetime 'x instead of Fn(Foo<'a>) for any lifetime 'a on your closure.
Let's see if we can replicate the error by defining a struct by hand (this requires a nightly compiler), so we can better understand what's going on. First, let's define the struct the correct way:
#![feature(fn_traits)]
#![feature(unboxed_closures)]
// Foo and IntoBox unchanged
struct Func;
impl<'a> FnOnce<(Foo<'a>,)> for Func {
type Output = String;
extern "rust-call" fn call_once(self, args: (Foo<'a>,)) -> String {
self.call(args)
}
}
impl<'a> FnMut<(Foo<'a>,)> for Func {
extern "rust-call" fn call_mut(&mut self, args: (Foo<'a>,)) -> String {
self.call(args)
}
}
impl<'a> Fn<(Foo<'a>,)> for Func {
extern "rust-call" fn call(&self, (i,): (Foo<'a>,)) -> String {
format!("{:?}", i)
}
}
fn main() {
let x = IntoBox::into_box(Func);
}
This Func struct compiles fine and behaves just like your original closure.
Now, let's break it:
impl FnOnce<(Foo<'static>,)> for Func {
type Output = String;
extern "rust-call" fn call_once(self, args: (Foo<'static>,)) -> String {
self.call(args)
}
}
impl FnMut<(Foo<'static>,)> for Func {
extern "rust-call" fn call_mut(&mut self, args: (Foo<'static>,)) -> String {
self.call(args)
}
}
impl Fn<(Foo<'static>,)> for Func {
extern "rust-call" fn call(&self, (i,): (Foo<'static>,)) -> String {
format!("{:?}", i)
}
}
What I've done here is that I've removed the <'a> on each impl, so that the impls are no longer generic over a lifetime, and I've replaced Foo<'a> with Foo<'static>. This means that now, the traits are only implemented when the "closure"'s argument is a Foo<'static>.
This fails to compile with the following errors:
error[E0271]: type mismatch resolving `for<'r> <Func as std::ops::FnOnce<(Foo<'r>,)>>::Output == std::string::String`
--> <anon>:51:13
|
51 | let x = IntoBox::into_box(Func);
| ^^^^^^^^^^^^^^^^^ expected bound lifetime parameter , found concrete lifetime
|
= note: concrete lifetime that was found is the static lifetime
= note: required because of the requirements on the impl of `IntoBox` for `Func`
= note: required by `IntoBox::into_box`
error[E0277]: the trait bound `for<'r> Func: std::ops::Fn<(Foo<'r>,)>` is not satisfied
--> <anon>:51:13
|
51 | let x = IntoBox::into_box(Func);
| ^^^^^^^^^^^^^^^^^ the trait `for<'r> std::ops::Fn<(Foo<'r>,)>` is not implemented for `Func`
|
= help: the following implementations were found:
= help: <Func as std::ops::Fn<(Foo<'static>,)>>
= note: required because of the requirements on the impl of `IntoBox` for `Func`
= note: required by `IntoBox::into_box`
The first error is the same, but instead of an internal name like '_#29r, the compiler mentions the static lifetime, because that's what I used here. I suspect that what the compiler is doing with the closure that doesn't compile in your code is similar to my second set of impls, just that instead of 'static, it's some other concrete lifetime that we can't name in Rust. The second error is different but means pretty much the same thing.

Related

Can I store an `impl Future` as a concrete type?

tokio::net::TcpStream::connect is an async function, meaning it returns an existential type, impl Future. I would like to store a Vec of these futures in a struct. I've found many questions where someone wants to store multiple different impl Futures in a list, but I only want to store the return type of one. I feel like this should be possible without Box<dyn Future> as I am really only storing a single concrete type, but I cannot figure out how without getting found opaque type errors.
It is possible with the nightly feature min_type_alias_impl_trait. The trick is to create a type alias, and a dummy function from which the compiler can infer a defining use.
#![feature(min_type_alias_impl_trait)]
use tokio::net::TcpStream;
use core::future::Future;
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn __tcp_stream_connect_defining_use() -> TcpStreamConnectFut {
TcpStream::connect("127.0.0.1:8080")
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
This compiles, but does not work as expected:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
}
}
error[E0308]: mismatched types
--> src/lib.rs:18:35
|
6 | type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
| ------------------------------------------------ the expected opaque type
...
18 | self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
|
= note: while checking the return type of the `async fn`
= note: expected opaque type `impl Future` (opaque type at <src/lib.rs:6:28>)
found opaque type `impl Future` (opaque type at </playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.7.1/src/net/tcp/stream.rs:111:56>)
= help: consider `await`ing on both `Future`s
= note: distinct uses of `impl Trait` result in different opaque types
Using the dummy function we created does work:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(__tcp_stream_connect_defining_use());
}
}
So we can just create wrapper functions:
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
Except...
error: type parameter `A` is part of concrete type but not used in parameter list for the `impl Trait` type alias
--> src/main.rs:9:74
|
9 | fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
| __________________________________________________________________________^
10 | | TcpStream::connect(addr)
11 | | }
| |_^
We could just use String or &'static str, and the entire thing compiles:
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect(addr: &'static str) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
You can also add a generic parameter to the type alias itself, but that probably doesn't make sense in this case:
type TcpStreamConnectFut<A> = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut<A> {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut<&'static str>>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
So it is possible, but there are a couple of restrictions. I'm not sure how many of these are bugs, and how much of it is intentional behavior. There has been discussion about a typeof operator to make this easier, but this is what we've got at the moment.
You can just use a good old Vec, just like that:
use core::future::Future;
use tokio::net::TcpStream;
fn just_vec() -> Vec<impl Future<Output = std::io::Result<TcpStream>>> {
let mut v = Vec::new();
// connect to several streams
v.push(TcpStream::connect("127.0.0.1:8080"));
v.push(TcpStream::connect("127.0.0.2:8080"));
v
}
However, it gets more tricky if you want to store it in a struct, because unlike above, where concrete type can be inferred, you need to be more explicit with structs.
One stable way to do this, is to use a generic struct. This actually very similar to storing a closure (where you don't have a concrete type either).
use core::future::Future;
use tokio::net::TcpStream;
use tokio::io::AsyncWriteExt;
struct Foo<T> {
connections: Vec<T>,
}
/// This is just like any other vec-wrapper
impl<T> Foo<T> {
pub fn new() -> Self {
Self {
connections: Vec::new(),
}
}
pub fn push(&mut self, conn: T) {
self.connections.push(conn);
}
}
/// Some more specific functios that actually need the Future
impl<T> Foo<T> where T: Future<Output = std::io::Result<TcpStream>> {
pub async fn broadcast(self, data: &[u8]) -> std::io::Result<()> {
for stream in self.connections {
stream.await?.write_all(data).await?
}
Ok(())
}
}
async fn with_struct() -> std::io::Result<()> {
let mut foo = Foo::new();
// connect to several streams
foo.push(TcpStream::connect("127.0.0.1:8080"));
foo.push(TcpStream::connect("127.0.0.2:8080"));
// Do something with the connections
foo.broadcast(&[1,2,3]).await
}

lifetime must be valid for the static lifetime so that the types are compatible

I'm aware of Where did the 'static lifetime come from and Cannot infer an appropriate lifetime for autoref due to conflicting requirements。
But I still do not understand the problem I've encountered:
use std::ops::Index;
trait Stack<T> {
fn as_slice(&self) -> &[T];
}
impl<T> Index<usize> for Stack<T> {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_slice()[i]
}
}
trait Core {
fn stack(&self) -> &Stack<usize>;
fn bad(&mut self) -> usize {
self.stack()[0]
}
fn good(&mut self) -> usize {
self.stack().as_slice()[0]
}
}
fn main() {}
In the code above, good() gives no error, but bad() complains with:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:18:14
|
18 | self.stack()[0]
| ^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 17:5...
--> src/main.rs:17:5
|
17 | / fn bad(&mut self) -> usize {
18 | | self.stack()[0]
19 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:18:9
|
18 | self.stack()[0]
| ^^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the types are compatible:
expected std::ops::Index<usize>
found std::ops::Index<usize>
There is no Box in this code, and I do not know where the static lifetime comes from.
Edit: through try and error I found the compiler assume Stack + 'static. The following code compiles. But why? Please point me to some document.
impl<'b, T> Index<usize> for Stack<T> + 'b {
type Output = T;
fn index<'a>(&'a self, i: usize) -> &'a T {
&self.as_slice()[i]
}
}
You are implementing a Trait (Index) for a Trait Object (Stack<T>).
The Rust reference states:
Since a trait object can contain references, the lifetimes of those references need to be expressed as part of the trait object. This lifetime is written as Trait + 'a. There are defaults that allow this lifetime to usually be inferred with a sensible choice.
If You don't define a lifetime the compiler assume a default, in this case it is assumed 'static (see here for a detailed explanation)
Your code is equivalent to:
impl<T> Index<usize> for Stack<T> + 'static {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_slice()[i]
}
}
To resolve the cannot infer an appropriate lifetime for autoref due to conflicting requirements compilation error just declare that stack() method returns a trait object with 'static lifetime.
trait Core {
fn stack(&self) -> &'static Stack<usize>;
fn bad(&mut self) -> usize {
self.stack()[0]
}
fn good(&mut self) -> usize {
self.stack().as_slice()[0]
}
}
Otherwise declare a generic lifetime for the Stack<T> trait object that impl Index:
impl<'a, T> Index<usize> for Stack<T> + 'a {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_slice()[i]
}
}
trait Core {
fn stack(&self) -> &Stack<usize>;
fn bad(&mut self) -> usize {
self.stack()[0]
}
fn good(&mut self) -> usize {
self.stack().as_slice()[0]
}
}
At this point you should ask: why using as_slice() in good() works and using index() in bad() does not?
To make same sense of it try read the comments embedded in the MVCE below.
use std::ops::Index;
trait Stack<T> {
fn as_slice(&self) -> &[T];
}
// equivalent to: impl<T> Index<usize> for Stack<T>
// just to expose the conflicting requirements error
// the right declaration is:
// impl<'a, T> Index<usize> for Stack<T> + 'a
impl<T> Index<usize> for Stack<T> + 'static {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_slice()[i]
}
}
trait Core {
fn stack(&self) -> &Stack<usize>;
fn bad<'a>(&'a mut self) -> usize {
//self.stack()[0] syntactic sugar for:
*self.stack().index(0)
// self.stack() returns a trait object with a lifetime bound != 'static
// but Stack impl for Index requires a 'static lifetime bound:
// COMPILE ERROR: cannot infer an appropriate lifetime for
// autoref due to conflicting requirements
}
fn good<'a>(&'a mut self) -> usize {
// self.stack() returns a trait object with 'a lifetime bound
// this is congruent with as_slice() lifetime requirements
// see Catasta::as_slice() impl below
// NO COMPILE ERROR
self.stack().as_slice()[0]
}
}
struct Catasta<T> {
pila: [T;4]
}
impl<T> Stack<T> for Catasta<T> {
fn as_slice<'a>(&'a self) -> &'a [T] {
&self.pila
}
}
struct RealCore {
stk: Catasta<usize>
}
impl Core for RealCore {
fn stack(&self) -> &Stack<usize> {
&self.stk
}
}
fn main() {
let mut core = RealCore {stk: Catasta {pila: [100, 2, 3, 4]} };
println!("pos [0] item: {}", core.good());
}

Fn as trait concrete lifetime required

I want to use an API that I can modify reg:
struct Ctx;
trait Foo {}
trait Ex {
fn do_<'a>(&self, cx: &'a mut Ctx) -> Box<Foo + 'a>;
}
impl<F> Ex for F
where
F: for<'a> Fn(&'a mut Ctx) -> Box<Foo + 'a>,
{
fn do_<'a>(&self, ecx: &'a mut Ctx) -> Box<Foo + 'a> {
(*self)(ecx)
}
}
fn reg<F>(name: &str, ext: F)
where
F: Ex + 'static,
{
}
//My code starts here
struct Boo;
impl Boo {
fn f1<'a>(&self, cx: &'a mut Ctx) -> Box<Foo + 'a> {
unimplemented!();
}
}
fn main() {
let boo = Boo;
reg("aaa", move |cx| boo.f1(cx));
}
But I got an error:
error[E0271]: type mismatch resolving `for<'a> <[closure#src/main.rs:33:16: 33:36 boo:_] as std::ops::FnOnce<(&'a mut Ctx,)>>::Output == std::boxed::Box<Foo + 'a>`
--> src/main.rs:33:5
|
33 | reg("aaa", move |cx| boo.f1(cx));
| ^^^ expected bound lifetime parameter 'a, found concrete lifetime
|
= note: concrete lifetime that was found is lifetime '_#9r
= note: required because of the requirements on the impl of `Ex` for `[closure#src/main.rs:33:16: 33:36 boo:_]`
= note: required by `reg`
error[E0281]: type mismatch: `[closure#src/main.rs:33:16: 33:36 boo:_]` implements the trait `std::ops::Fn<(&mut Ctx,)>`, but the trait `for<'a> std::ops::Fn<(&'a mut Ctx,)>` is required
--> src/main.rs:33:5
|
33 | reg("aaa", move |cx| boo.f1(cx));
| ^^^ -------------------- implements `std::ops::Fn<(&mut Ctx,)>`
| |
| requires `for<'a> std::ops::Fn<(&'a mut Ctx,)>`
| expected concrete lifetime, found bound lifetime parameter 'a
|
= note: required because of the requirements on the impl of `Ex` for `[closure#src/main.rs:33:16: 33:36 boo:_]`
= note: required by `reg`
How can I fix this?
In real code my struct Boo contains some data,
and want to call reg for it twice, so I not implement trait Ex, but
try to use closure.
Looks like issue #38714.
While it is being fixed, you can directly implement Ex for Boo.
impl Ex for Boo {
fn do_<'a>(&self, ecx: &'a mut Ctx) -> Box<Foo + 'a> {
self.f1(ecx)
}
}
fn main() {
let boo = Boo;
reg("aaa", boo);
}
In real code my struct Boo contains some data, and want to call reg for it twice, so I not implement trait Ex, but try to use closure.
You'll not be able to do that with the code you provided. move |cx| boo.f1(cx) moves boo into the closure, and you can't use boo after that.
If you want to share data, you'll need to use Rc in Boo.

Using a borrow as an associated trait type

This code works:
struct Test {
val: String,
}
impl Test {
fn mut_out(&mut self) -> &String {
self.val = String::from("something");
&self.val
}
}
However, a more generic implementation does not work:
struct Test {
val: String,
}
trait MutateOut {
type Out;
fn mut_out(&mut self) -> Self::Out;
}
impl MutateOut for Test {
type Out = &String;
fn mut_out(&mut self) -> Self::Out {
self.val = String::from("something");
&self.val
}
}
The compiler cannot infer a lifetime for the string borrow:
error[E0106]: missing lifetime specifier
--> src/main.rs:13:16
|
11 | type Out = &String;
| ^ expected lifetime parameter
I cannot figure out a way to explicitly state the lifetime of the borrow, as it depends on the function itself.
Taking inspiration from the Deref trait, you can remove the reference from the associated type and instead just note in the trait that you want to return a reference to the associated type:
trait MutateOut {
type Out;
fn mut_out(&mut self) -> &Self::Out;
}
impl MutateOut for Test {
type Out = String;
fn mut_out(&mut self) -> &Self::Out {
self.val = String::from("something");
&self.val
}
}
Here it is in the playground. Given that your function name was mut_out, if a mutable reference is what you were after, here is a playground example with that as well.

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