简体   繁体   中英

Cast a generic param to a specific type

If I have two structs:

struct A {...}
struct B {...}

and a generic function fn f<T>(param: T) that I call with passing either a reference to A or B , is there a way in that function to have something like this (pseudo code):

if param is A {
    // do something with "param as A", like this:
    let a: A = (A) param;
    // ...
}

In languages like Java, C#, etc. I would simply check if an object is an instance of A and if so, cast it to A as in the example above. How can I do something like that in Rust? I know I could put some type-specific logic in a trait, but I'm specifically asking for a simpler, more direct way.

You can do exactly what you are asking for using trait objects of the Any trait, eg

use std::any::Any;

#[derive(Debug)]
struct A;
#[derive(Debug)]
struct B;

fn foo(param: &dyn Any) {
    if let Some(a) = param.downcast_ref::<A>() {
        dbg!(a);
    }
    if let Some(b) = param.downcast_ref::<B>() {
        dbg!(b);
    }
}

However, for common use cases there are more idiomatic, ergonomic and efficient solutions. You mentioned implementing a common trait on A and B in your question, which is one approach. Another approach is defining an enum with variants for all types you want to support:

enum MyEnum {
    A(A),
    B(B),
}

fn bar(param: MyEnum) {
    match param {
        MyEnum::A(a) => { dbg!(a); },
        MyEnum::B(b) => { dbg!(b); },
    }
}

What you're looking for is not yet quite available in current Rust, but will become available once the specialization feature lands. Using specialization, and testable on current nightly, your function would look like this:

#![feature(min_specialization)]

struct A {}
struct B {}

fn f<T>(param: Vec<T>) {
    trait Detect {
        fn detect(&self);
    }
    impl<T> Detect for Vec<T> {
        default fn detect(&self) {
            println!("I am something else");
        }
    }
    impl Detect for Vec<A> {
        fn detect(&self) {
            println!("I am a Vec<A>");
        }
    }
    param.detect();
    // ...
}

fn main() {
    f(Vec::<A>::new());
    f(Vec::<B>::new());
}

Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM