Home > database >  Cast a generic param to a specific type
Cast a generic param to a specific type

Time:10-26

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.

CodePudding user response:

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

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); },
    }
}

CodePudding user response:

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

  • Related