Add support for conditional delegation
vigna opened this issue · 6 comments
We are in a situation in which we have frequently this pattern: there's a struct S <B> with a field f of type B. We need to implement trait T on S conditionally on the fact that B implements T, and to do so by delegating T's method to the implementation of T provided by field f. playground
It seems to me that presently this use case is not covered by ambassador, but all the pieces are there. Would it be difficult to implement?
I think this is already supported, based on you playground ambassador supports:
use ambassador::{delegatable_trait, Delegate};
#[derive(Delegate)]
#[delegate(T)]
struct S<B> {
f: B,
}
/*
Expands to:
impl<B, > T for S<B> where B: T {
#[inline]
fn foo(&self) {
self.f.foo()
}
}
*/
#[delegatable_trait]
trait T {
fn foo(&self);
}
impl T for u32 {
fn foo(&self) {
println!("{self}")
}
}
fn main() {
let s = S { f: 5 };
s.foo() //Prints: 5
}
More generally #[delegate(Trait)]
adds a where clause where Type: Trait
where Type
is the type of the field being delegate to. This can be disabled using #[delegate(Trait, automatic_where_clause = "false")]
. If additional where clauses are required they can be added using #[delegate(Trait, where = "Type1: Trait1, Type2: Trait2, ...")]
where Type1
, Trait1
, Type2
, Trait2
, ..., can be arbitrary types and traits respectively (including generics).
Well... in theory. I have this trait
#[delegatable_trait]
#[autoimpl(for<T: trait + ?Sized> &T, &mut T, Box<T>)]
pub trait BitLength {
/// Returns a length in bits.
fn len(&self) -> usize;
}
But when I use
#[derive(Epserde, Debug, Clone, MemDbg, MemSize, Delegate)]
#[delegate(BitLength, target = "bits")]
pub struct Rank9<B = BitVec, C = Box<[BlockCounters]>> {
pub(super) bits: B,
pub(super) counts: C,
}
I get (cargo expand
)
impl<B, C> BitLength for Rank9<B, C>
where
B: BitLength,
{}
Is it possible that there is some interference from the other derive macros?
More in detail, this is the compiler output:
error: cannot find macro `ambassador_impl_BitLength` in this scope
--> src/rank_sel/rank9.rs:64:12
|
64 | #[delegate(BitLength, target = "bits")]
| ^^^^^^^^^
|
= help: have you added the `#[macro_use]` on the module/import?
help: consider importing this macro through its public re-export
|
9 + use crate::ambassador_impl_BitLength;
|
error[E0046]: not all trait items implemented, missing: `len`
--> src/rank_sel/rank9.rs:63:50
|
63 | #[derive(Epserde, Debug, Clone, MemDbg, MemSize, Delegate)]
| ^^^^^^^^ missing `len` in implementation
|
::: src/traits/rank_sel.rs:28:5
|
28 | fn len(&self) -> usize;
| ----------------------- `len` from trait
|
= note: this error originates in the derive macro `Delegate` (in Nightly builds, run with -Z macro-backtrace for more info)
Are you trying to use derive(Delegate)
in a different module that delegable(Trait)
? If so the problem might be related to #45
Well... yes. 😅 Usually traits are somewhere and implementations elsewhere. 🤷🏻
One vote for the comment in the other issue: this should be the first thing in the documentation.
I spent a lot of time writing my ad hoc delegation macros but if I can make this work I'll certainly switch to ambassador, it's a much better solution.
The use ambassador_impl*
is quite ugly and appears to use undocumented internals, which might be a problem for future compatibility. It should be output by the derive code IMHO.