How to make trait method take &Self parameter - rust

I'm trying to create a trait with some default method implementation. One of the methods has to take an instance of the same type and perform some computations.
Here is the simplistic example of what I'm trying to achieve:
struct A {
val: f32,
}
trait S {
fn val(&self) -> f32;
fn add(&self, other: &Self) -> f32 {
add(&self, other)
}
}
impl S for A {
fn val(&self) -> f32 {
self.val
}
}
fn add<T: S>(first: &T, second: &T) -> f32 {
first.val() + second.val()
}
This fails to compile with the error:
15 | | fn add(&self, other: &Self) -> f32 {
16 | | add(&self, other)
| | ^^^^^ expected `&Self`, found type parameter `Self`
17 | | }
I do not understand the error message, for other is of type &Self not Self, so why does compiler thinks otherwise?
If I change it to reference add(&self, &other) (which doesn't seem right, since other is already a reference type), I get another error:
|
16 | add(&self, &other)
| ^^^ the trait `S` is not implemented for `&Self`
...
26 | fn add<T: S>(first: &T, second: &T) -> f32 {
| - required by this bound in `add`
Is there a way to achieve this with default trait implementation, or this can work only with concrete types (like in this question: How do I implement the Add trait for a reference to a struct?)?
EDIT: If I call it like add(self, other), it tries to send the trait object, but I want to send the objects by reference. And really, I want to send concrete implementations and not trait objects.
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/main.rs:16:9
|
16 | add(self, other)
| ^^^ doesn't have a size known at compile-time

You're passing &self to add(), which makes it a double reference and the arguments to add() don't agree in types. The T of add is apparently determined by the first argument, which is why the compiler appears to expect the other argument to also have the &&Self parameter. (Read the note at the end of the error message.)
Call it with add(self, other) and it will compile. Note that you'll also need to opt out of the implicit Sized bound on add by adding + ?Sized.
// rest as in your code
trait S {
fn val(&self) -> f32;
fn add(&self, other: &Self) -> f32 {
add(self, other)
}
}
fn add<T: S + ?Sized>(first: &T, second: &T) -> f32 {
first.val() + second.val()
}
Playground

Related

Why does BitAnd::bitand() not allow references?

This code:
impl<I> BitAnd for BitSparseArray<I> where I: PrimInt + Unsigned + std::fmt::Debug {
type Output = Self;
fn bitand(&self, rhs: &Self) -> Self::Output {
assert_eq!(self.numbits, rhs.numbits);
...
}
}
fails to compile with:
error[E0053]: method `bitand` has an incompatible type for trait
--> src/lib.rs:44:5
|
44 | fn bitand(&self, rhs: &Self) -> Self::Output {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `BitSparseArray`, found reference
|
= note: expected fn pointer `fn(BitSparseArray<I>, BitSparseArray<I>) -> BitSparseArray<_>`
found fn pointer `fn(&BitSparseArray<I>, &BitSparseArray<I>) -> BitSparseArray<_>`
And a move operation would result without the reference, which means the user of this will lose the source structures to a move.
Why would there not be an option to do references here as well? Of course, I could use explicit functions to do what I want, but that would not be as cool of an approach!

Unexpected error when using explicit lifetime in template argument in Rust [duplicate]

I have a trait Matrix and generic function semi_def<T: Matrix>(x: &T) that I would like to operate on that trait. The function requires an operator trait, say Mul, be implemented on T. However, I can't seem to make the lifetimes happy if one of the references is to a local variable. How do I write the lifetimes for references in the type constraint when one of them is just a local temporary reference?
use std::ops::Mul;
trait Matrix: Clone {
fn transpose(self) -> Self;
}
#[derive(Clone)]
struct DenseMatrix {
n_rows: usize,
n_columns: usize,
elements: Vec<f64>,
}
impl Matrix for DenseMatrix {
fn transpose(self) -> Self {
unimplemented!()
}
}
impl<'a, 'b> Mul<&'b DenseMatrix> for &'a DenseMatrix {
type Output = DenseMatrix;
fn mul(self, _rhs: &'b DenseMatrix) -> Self::Output {
unimplemented!()
}
}
fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
&'a T: Mul<&'a T, Output = T>,
{
&(*x).clone().transpose() * x
}
fn main() {}
which gives this error:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:31:6
|
31 | &(*x).clone().transpose() * x
| ^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
32 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 27:1...
--> src/main.rs:27:1
|
27 | / fn semi_def<'a, T: Matrix>(x: &'a T) -> T
28 | | where
29 | | &'a T: Mul<&'a T, Output = T>,
30 | | {
31 | | &(*x).clone().transpose() * x
32 | | }
| |_^
You need higher-ranked trait bounds (HRTBs), which are described in the advanced Rust book Rustonomicon and well as on Stack Overflow. They allow a type constraint to say that trait must be implemented not just for references with a particular lifetime but for any lifetime. They use the where for<> syntax. Here is the function definition that says an implementation of Mul is needed for any two references to T:
fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
for<'b, 'c> &'b T: Mul<&'c T, Output = T>,
{
&(*x).clone().transpose() * x
}
Because one of the references actually has the lifetime 'a, not a local lifetime, this could be written with a slightly looser constraint:
fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
for<'b> &'b T: Mul<&'a T, Output = T>,
{
&(*x).clone().transpose() * x
}
This Q&A is based off a question I asked on the Rust users mailing, which I cleaned up and brought over here for future Rustaceans.

How to return an iterator over the keys of a HashMap from a trait implementation?

I'm trying to build a simple graph library in Rust. There is a trait Graph that any graph must implement. This trait has only one function at the moment, nodes, which allows iteration of the graph's nodes using a for-in loop.
An implementation of Graph, MapGraph, is a lightweight wrapper around a HashMap. MapGraph must implement the Graph trait method nodes. I'm having problems getting this to work.
Here's the code for Graph:
pub trait Graph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item = &N>>;
}
And here's the code for MapGraph:
use std::collections::HashMap;
use crate::rep::Graph;
pub struct MapGraph<N> {
map: HashMap<N, HashMap<N, ()>>
}
impl<N> MapGraph<N> {
pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
MapGraph { map }
}
}
impl<N> Graph<N> for MapGraph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item=&N>> {
let keys = self.map.keys();
Box::new(keys)
}
}
The compiler gives this error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/lib.rs:19:29
|
19 | let keys = self.map.keys();
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 18:5...
--> src/lib.rs:18:5
|
18 | / fn nodes(&self) -> Box<dyn Iterator<Item = &N>> {
19 | | let keys = self.map.keys();
20 | |
21 | | Box::new(keys)
22 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:19:20
|
19 | let keys = self.map.keys();
| ^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
I've found other references to this error, but those cases don't seem to look like the one I have here.
I'm using Box because the Graph trait has a function that itself returns a trait. What is the correct way to return an Iterator (or any other trait)? gives this approach as one option, and I haven't been able to implement any of the the others. If there's another way to do this, that would be fine.
What are my options for resolving this specific problem?
It works if you explicitly specify that the trait object (dyn Iterator) that you are returning contains references that are tied to the lifetime of self.
Without adding this bound, the compiler cannot infer from the function signature that the iterator cannot be used after self is moved or destroyed. Because the compiler cannot infer this, it cannot safely use self.map.keys() in the function's output.
Working example with this bound added:
pub trait Graph<N> {
fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a>;
}
use std::collections::HashMap;
pub struct MapGraph<N> {
map: HashMap<N, HashMap<N, ()>>,
}
impl<N> MapGraph<N> {
pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
MapGraph { map }
}
}
impl<N> Graph<N> for MapGraph<N> {
fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a> {
let keys = self.map.keys();
Box::new(keys)
}
}
Playground
I had thought that a bound of Item = &'a N would also be required, but I guess that's already covered by the "+ 'a"...
N.B. that to make sense of an error like:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
you have to understand that the compiler, for ergonomic reasons, automatically adds a + 'static lifetime qualifier to any unqualified trait object. This means that an unqualified Box<dyn MyTrait> is transformed by the compiler into a Box<(dyn MyTrait + 'static)>, which in turn means that the object cannot contain any references except those that last for the lifetime of the entire program.
With this in mind you can see why self.map.keys() does not fit this strict bound, and a more specific explicit bound is required.

What happens when a Rust struct contains a lifetimed trait?

Ok, so I'm a total Rust newbie, and I'm experimenting with Rocket. That web framework passes a Form<MyStruct>, and I want to transfer that MyStruct into my own custom struct.
struct Consumer<T> {
d: T,
}
impl<T> Consumer<T> {
fn new(form: Form<T>) -> Self {
Consumer { d: form.into_inner() }
}
}
That doesn't work of course, I get:
the trait `rocket::request::FromForm<'_>` is not implemented for `T`
Next attempt:
impl<T> Consumer<T> where T: FromForm {
fn new(form: Form<T>) -> Self {
Consumer { d: form.into_inner }
}
}
Uh oh:
impl<T> Consumer<T> where T: FromForm {
^^^^^^^^ expected lifetime parameter
So now I find myself completely unable to fix this! The best I can come up with is:
impl<'f, T> Consumer<T> where T: FromForm<'f> {
fn new(form: Form<T>) -> Self {
Consumer { d: form.into_inner }
}
}
But that results in this error:
51 | fn new(form: Form<T>) -> Self {
| _________^
52 | | Consumer { d: form.into_inner }
53 | | }
| |_________^ lifetime mismatch
= note: expected type `rocket::request::FromForm<'_>`
found type `rocket::request::FromForm<'f>`
Verifiable example: https://hastebin.com/eqihaqodux.makefile
Form also has a lifetime parameter. If you tie it to the lifetime of FromForm, then you'll move forward a little:
impl<'f, T> Consumer<T> where T: FromForm<'f> {
fn new(form: Form<'f, T>) -> Self {
Consumer(form.into_inner())
}
fn get(&self) -> &T {
&self.0
}
}
As a general rule, if you return an object that depends on data in another object, then you'll need to link their lifetimes together like this.
At this point, you'll see another error, which conveniently gives you all the information you need to fix it:
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:50:17
|
48 | impl<'f, T> Consumer<T> where T: FromForm<'f> {
| - help: consider adding an explicit lifetime bound `T: 'static`...
49 | fn new(form: Form<'f, T>) -> Self {
50 | Consumer(form.into_inner())
| ^^^^^^^^^^
|
note: ...so that the type `T` will meet its required lifetime bounds
--> src/main.rs:50:17
|
50 | Consumer(form.into_inner())
| ^^^^^^^^^^
The into_inner method on Form requires that it's type parameter T has the 'static lifetime, and the error message suggests adding this constraint.
With these changes, it will compile:
impl<'f, T: 'static> Consumer<T> where T: FromForm<'f> {
fn new(form: Form<'f, T>) -> Self {
Consumer(form.into_inner())
}
fn get(&self) -> &T {
&self.0
}
}

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