Skip to main content

try_as_dyn

Function try_as_dyn 

Source
pub const fn try_as_dyn<'a, T: ?Sized + 'a, U: TryAsDynCompatible<'a> + ?Sized>(
    t: &T,
) -> Option<&U>
🔬This is a nightly-only experimental API. (try_as_dyn #144361)
Expand description

Returns Some(&U) if T can be coerced to the dyn trait type U. Otherwise, it returns None.

§Run-time failures

There are multiple ways to get a None, and you need to manually analyze which one it is, as the compiler does not provide any help here.

  • T does not implement Trait at all,
  • T’s impl for Trait is not fully generic,
  • T’s impl for Trait is a builtin impl (e.g. dyn Debug implements Debug)

There is some detailed documentation about this feature at https://doc.rust-lang.org/unstable-book/language-features/try_as_dyn.html But the gist is summarized below:

§Lifetime-independent impls

try_as_dyn does not have access to lifetime information, thus it cannot differentiate between 'static, other lifetimes, and can’t reason about outlives bounds on impls. Thus we can only accept impls that do not have 'static lifetimes, or outlives bounds of any kind. You can have simple trait bounds, and the compiler will transitively only use impls of those simple trait bounds that satisfy the same rules as the main trait you’re converting to.

An example of a legal impl is:

impl<'a, 'b, T: Debug, U: Display> Trait<'a, T> for Type<'b, U> {}

Impls without generic parameters at all are also legal, as long as they contain no 'static lifetimes.

§Builtin impls

Builtin impls (like impl Debug for dyn Debug) have various obscure rules and often are not fully generic. To simplify reasoning about what is allowed and what not, all builtin impls are rejected and will neither directly nor indirectly contribute to a Some result.

§Compile-time failures

Determining whether T can be coerced to the dyn trait type U requires compiler trait resolution. In some cases, that resolution can exceed the recursion limit, and compilation will fail instead of this function returning None.

The input type T must outlive the lifetime 'a on the dyn Trait + 'a. This is basically the same rule that forbids let x: &dyn Trait + 'static = &&some_local_variable; So if you see borrow check errors around try_as_dyn, think about whether a normal unsizing coercion would be possible at all if you were using concrete types or had bounds on the input type.

§Examples

#![feature(try_as_dyn)]

use core::any::try_as_dyn;

trait Animal {
    fn speak(&self) -> &'static str;
}

struct Dog;
impl Animal for Dog {
    fn speak(&self) -> &'static str { "woof" }
}

struct Rock; // does not implement Animal

let dog = Dog;
let rock = Rock;

let as_animal: Option<&dyn Animal> = try_as_dyn::<Dog, dyn Animal>(&dog);
assert_eq!(as_animal.unwrap().speak(), "woof");

let not_an_animal: Option<&dyn Animal> = try_as_dyn::<Rock, dyn Animal>(&rock);
assert!(not_an_animal.is_none());