问题
I have:
use std::ops::{Add, Div, Mul, Neg, Sub};
pub trait Hilbert: Add + Sub + Mul + Div + Neg + Mul<f64> + Div<f64> + Sized {
fn dot(&self, other: &Self) -> f64;
fn magnitude(&self) -> f64;
}
fn g<T: Hilbert>(x: T) -> f64 {
return (x * 2.0).dot(x);
}
...which yields:
error[E0599]: no method named `dot` found for type `<T as std::ops::Mul<f64>>::Output` in the current scope
--> src/main.rs:9:22
|
9 | return (x * 2.0).dot(x);
| ^^^
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `dot`, perhaps you need to implement it:
candidate #1: `Hilbert`
I interpret this to mean that Rust can't guarantee that the type T
, which has trait Hilbert
, has an implementation of std::ops::Mul
whose ::Output
type is equal to T
(a Hilbert
).
But I know (and / or wish to demand) that this is the case for all Hilbert
s, so that functions like g()
are possible to write.
I would think to impl std::ops::Mul::Output
for Hilbert
:
impl<T: Hilbert> Mul<f64> for T {
type Output = T;
}
...but this has the simultaneous problems that (a) I can't "partially implement" a trait, and would be forced to produce a generic implementation of the function Mul::mul()
for all Hilberts
, but the actual implementation of Mul::mul()
will depend on the specific implementation of Hilbert
; and (b) it seems I am not allowed to write this trait at all:
error[E0210]: 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
--> src/main.rs:12:1
|
12 | / impl<T: Hilbert> Mul<f64> for T {
13 | | type Output = T;
14 | | }
| |_^
How do I persuade Rust that Hilbert
* f64
-> Hilbert
must hold?
回答1:
How do I persuade Rust that
Hilbert * f64
->Hilbert
must hold?
You add a trait bound <T as Mul<f64>>::Output: Hilbert
. However, doing so will reveal further issues in your design:
Hilbert.dot()
takes the second argument as reference, not by value. But changing the relevant line to(x * 2.0).dot(&x)
leads to another error: "expected associated type, found type parameter".- This one is because you defined
dot
to takeSelf
, but there could be different implementations ofHilbert
you want to multiply.dot
needs to be generic:fn dot<H: Hilbert>(&self, other: &H) -> f64;
- Finally, the borrow checker hits:
(x * 2.0).dot(&x)
won't let you usex
twice, becausemul
takes its argument by value. You will either have to add a boundMul<&'a Self>
to be able to pass in a reference (which infects your API with lifetime parameters) or makex
cloneable (I don't think copyable would apply).
Applying all of the above results in this working(?) compilable code:
pub trait Hilbert: Add + Sub + Mul + Div + Neg + Mul<f64> + Div<f64> + Sized {
fn dot<H: Hilbert>(&self, other: &H) -> f64;
fn magnitude(&self) -> f64;
}
fn g<T: Hilbert + Clone>(x: T) -> f64
where
<T as Mul<f64>>::Output: Hilbert,
{
(x.clone() * 2.0).dot(&x)
}
If Hilbert.dot
should not be generic because different implementations of Hilbert
do not need to interact, the code can be slightly simpler (in terms of trait bounds):
pub trait Hilbert:
Add + Sub + Mul + Div + Neg + Mul<f64, Output = Self> + Div<f64, Output = Self> + Sized
{
fn dot(&self, other: &Self) -> f64;
fn magnitude(&self) -> f64;
}
fn g<T: Hilbert + Clone>(x: T) -> f64 {
(x.clone() * 2.0).dot(&x)
}
However, from what I know about the Hilbert transform, this latter case seems unlikely to be useful.
来源:https://stackoverflow.com/questions/49973888/how-do-i-specify-the-expected-result-of-a-stdopsmul-in-a-trait-bound