Upcast with `impl` abstraction

I am not good at English. Sorry if there are any funny expressions.

How do I upcast with impl abstraction?
I tried to mimic the dyn abstraction &x as &dyn TargetTrait.
But x as impl TargetTrait results in a compile error.

Using macros seems to be an alternative. But do I really need to do that? Maybe I'm missing some standard, or am doing some misplaced activity in the first place.

BTW, my purpose for upcasting is to improve code readability and to narrow down IDE auto-completion. I do this occasionally in other languages. And I would like to do the same in Rust.

macro_rules! upcast {
    ($x:expr, $t:path) => {
        {
            fn cast<T: $t>(x: T) -> impl $t {x}
            cast($x)
        }
    }
}

fn main() {
    // ❌ NG ... [E0562]: `impl Trait` is not allowed in cast expression types.
    // let x = MyType() as impl TargetTrait;

    // ✅ OK ... But Is there a more standard way to do this?
    let x = upcast!(MyType(), TargetTrait);

    assert_eq!(x.a(), "TargetTrait::a");
    assert_eq!(x.b(), "TargetTrait::b");
}

struct MyType();
impl TargetTrait for MyType {}
impl _UnusedTrait for MyType {}

trait TargetTrait {
    fn a(&self) -> &'static str { "TargetTrait::a" }
    fn b(&self) -> &'static str { "TargetTrait::b" }
}

trait _UnusedTrait {
    fn a(&self) -> &'static str { "_UnusedTrait::a" }
    fn b(&self) -> &'static str { "_UnusedTrait::b" }
}

Nothing about this is upcasting.

Anyway, impl Trait in bindings would be the cleaner way...

let x: impl Display = String::new();

...but the unstable implementation was removed, and I don't know if or when it's coming back. In the meanwhile, your macro seems fine to me.


More about "upcasting"

Rust traits are not about sub/super-typing. Coercing to dyn Trait isn't really upcasting, despite the terminology, because dyn Trait isn't a supertype of all Trait implementors.

Returning an -> impl Trait type is also not upcasting. It's just an opaque version of the returned type. It's still the same type "underneath".

use std::fmt::Display;
fn foo() -> impl Display { String::new() }

fn main() {
    // prints: alloc::string::String
    println!("{}", std::any::type_name_of_val(&foo()));
}

So "casting to impl Trait" doesn't really make sense. Either you're in a defining scope of an impl Trait and everything has to be the same type, or you're not in a defining scope and you don't know what the concrete type is.

3 Likes

Thanks for your reply.

At a glance, "impl Trait in bindings" is very good for me.
It is sad that it does not seem to be realized in the near future.
However, it is good to know that there are no errors in my macro.

About my misuse of the term "upcast". My apologies.
I used it easily because I thought it seems like "upcast" in other languages.
I am still often confused by the subtle differences between Rust and other languages.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.