You can put a where clause on the trait itself, but that appears to still require propagating the constraints onward. Your best choice is probably to require the trait to return a Result<(), Error> directly.
Alternatively, you can explicitly convert the result instead of using ?’s conversion:
struct Foo<T: Clone>(T);
fn foo<T>(x: Foo<T>) {
let Foo(y) = x;
let z = y.clone();
}
without putting a trait constraint on the parameter T in foo as well.
One could expect this to work without the constraint as a parameter x: Foo<T> can only exist if T: Clone is satisfied. But the way Rust traits on datatypes it is essentially the same way that Haskell used to do it(it’s deprecated and considered a misfeature in Haskell), heck, you cannot even do this:
I don’t know, I have it on my TODO list (or rather my personal Rust feature wishlist) to eventually start an IRLO discussion about this. I wasn’t aware this problem applies to traits, too, which makes it worse IMO.
To elaborate: They have a different Feature, called GADTs, with a very similar syntax that makes things like this actually work with datatypes, like this
data Foo t = Eq t => Foo t
foo :: Foo t -> Bool
foo x =
case x of
Foo y -> y == y
struct Foo<T: Eq>(T);
// does not work, needs <T: Eq>
fn foo<T>(x: Foo<T>) -> bool {
match x {
Foo(y) => y == y
}
}
And for traits (in Haskell called “(type) classes”) the problem never existed at all.
data Error
-- the `--` means this is a comment
-- recreating the `From` trait
class From b a where -- corresponds to Rusts `A: From B`
from :: b -> a
instance From a a where
from x = x
-- recreating the question mark operator
-- `Either b a` is like `Result<A,B>`
-- `Left` ≙ `Err`, `Right` ≙ `Ok`
(?) :: From b c => Either b a -> Either c a
(?) (Left err) = Left (from err)
(?) (Right x) = Right x
-----------------------------------------------------------
-- like: `trait Foo where Error: From<Self::Err> {`
-- but actually more like a generalized supertrait feature
class From (Err a) Error => Foo a where
type Err a
test :: a -> Either (Err a) ()
run :: Foo a => a -> Either Error ()
run t = do
(test t ?)
Right ()