Currently when you implement Deref<T>
on a type A
, you can use all the functions defined both by T
and by the traits implemented by T
.
For example you can do something like this:
use std::collections::Collection;
struct Foo(Vec<int>);
impl Deref<Vec<int>> for Foo {
fn deref(&self) -> &Vec<int> {
let &Foo(ref val) = self;
val
}
}
fn main() {
let f = Foo(Vec::new());
println!("{}", f.is_empty());
}
But let’s say you have a function that awaits an object which implements Collection
. You can’t directly pass a Foo
or you would get error: failed to find an implementation of trait core::collections::Collection for Foo
.
The call can still be achieved by calling needs_collection(&*foo)
instead of just needs_collection(&foo)
.
If I now implement MyCustomTrait
for Foo
, calling needs_mycustomtrait
can be done with needs_mycustomtrait(&foo)
.
But what if a function requests an object which implements both Collection
and MyCustomTrait
? Well, currently you can’t call it with a Foo
at all, even though you can call methods from both Collection
and MyCustomTrait
on the foo
object itself.
My proposed solution
Allow directly passing a Foo
to a function which awaits a Collection
. More generally, objects which implement Deref<T>
and/or DerefMut<T>
should automatically be treated as if they implemented all the traits that T
implements.
This should be recursive. If A implements Deref<T>
and T
implements Deref<U>
, then A
implements the traits of U
.
Real-life motivation
The reason why I’m opening this thread is this reddit post.
The idea is to implemented a web framework. When a request is received by the server, a BaseRequest
is created. Then it is turned into a WithSession<BaseRequest>
, which is then turned into a WithDatabase<WithSession<BaseRequest>>
, which is then turned into a WithAuthenticationAttempt<WithDatabase<WithSession<BaseRequest>>>
, which is then turned into a WithAuthenticatedUser<WithAuthenticationAttempt<WithDatabase<WithSession<BaseRequest>>>>
, etc.
Even when each element implements Deref
for its underlying object, there is currently no way to pass an object of this type to a fn handler<R: HasDatabase + HasAuthenticatedUser + HasBasicRequest>(request: R)
because of the issue explained in this thread, even though you can call methods from HasDatabase
, HasAuthenticatedUser
and HasBasicRequest
on the object.
Of course the stack of objects depends on what the framework user needs, that’s why you need to use traits and cannot simply use a type Rq = ...
. To answer a static content, you only need a HasBasicRequest
, but to query a user profile you need a HasDatabase + HasAuthenticatedUser + HasBasicRequest
, and to handle a file upload you need a HasFileUpload
.