Let's say I have a type T that implements 2 traits A and B.
I can instantiate the type with let t = T{};, sure, but then it can call methods from both traits later, and I don't want that. I want t to behave like it only implements A and notB. In other places, with other instances of T, I'll have the reverse restriction.
My first thought is to use trait objects. But that comes with dynamic dispatch, and boxed trait objects not implementing the trait by default, so I can't just pass them to functions like fn f(x: impl A) with no extra work or cost. Given that I know the actual type is T, and this is clear where the variable is created (like let t: Box<dyn A> = Box::new(T{});), is there really a need to bring dynamic dispatch into this at all? I don't even need the data to be on the heap instead of the stack.
Another option I'm seeing is newtypes. struct Ta(T); impl A for Ta {}
But then I have to do delegation for every single method of A, and I have to do that for every trait I'm dealing with (there are more than just the two).
Or have a function like this for every trait: fn as_a(&self) -> impl A where Self: Sized + Copy {*self}
(This approach is winning on convenience so far)
I suppose what I want to be able to do this: let t: impl A = T{} as A;
As a shortcut to get whatever is returned by the impl A return type.
Is there some sort of notation or wrapper type that does exactly what I want?
If I needed what you're asking for I'd probably use
fn impl_a<U: A>(U: u) -> impl A { u }
but maybe the method is more convenient for you.
Unfortunately I don't think I have anything to add that helps you, at least, not without knowing more. Some not-so-helpful comments follow...
Still not what you want, but &dyn Trait A would keep it on the stack. You'd still need + ?Sized on fa. (But it makes sense there anyway so far as your example shows.)
I don't have a solution, but I'm wondering how you could tell that it does not implement B, or what is the negative impact of implementing B? Are you just trying not to make a mistake and accidentally call a method that you shouldn't be calling?
One thought, if A xor B is used on a per module basis, is to only use A; or use B; in the modules that it should be used in, since traits have to be in scope to be called. But you could still make a mistake if your IDE auto-imports the trait when you try to call it.
A method returning impl $the_trait for every trait. Generated by macro for convenience.
Then I can just do let t = A::as_trait(T{}); to get what I want.
I can live with one-liner macros in every trait definition, but is there a way to get the trait identity automatically in the macro? I'd rather the macro invocations to simply be fn_as_trait!().
Code:
macro_rules! fn_as_trait {
($the_trait:ident) => {
fn as_trait(self) -> impl $the_trait where Self: Sized {self}
}
}
trait A {
fn a(&self) {}
fn_as_trait!(A);
}
trait B {
fn b(&self) {}
fn_as_trait!(B);
}
fn fa<U: A>(_x: &U) {}
fn fb<U: B>(_x: &U) {}
struct T {}
impl A for T {}
impl B for T {}
fn main() {
let t = T{};
t.a();
t.b();
fa(&t);
fb(&t);
let ta = A::as_trait(T{});
ta.a();
// ta.b(); // Error (good)
fa(&ta);
// fb(&ta); // Error (good)
let tb = B::as_trait(T{});
// tb.a(); // Error (good)
tb.b();
// fa(&tb); // Error (good)
fb(&tb);
}