将具有泛型类型参数的结构转换为特定类型

Cast a struct with a generic type parameter to a specific type

注意:今天早些时候我问了一个similar question,但是这个有很大的不同,所以我把它作为一个单独的问题发布。

如果我有两个(或更多)结构:

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

和我通过传递 AB (或与此相关的其他类型)的向量调用的通用函数 fn f<T>(param: Vec<T>) ,该函数中有没有办法有这样的东西(伪代码):

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

通常在 OOP 语言中,我基本上可以检查向量参数或其元素之一的类型(假定向量不为空)并转换参数。

在 Rust 中,是否有 简单直接 方法来实现,w/o 牺牲性能和 w/o 需要更改函数签名或在函数外编写任何代码(因此没有枚举包装器、特征、dyn/runtime“魔法”等)。

如果您能够约束 T: 'static

,您仍然可以使用链接问题中所示的 Any 方法
use std::any::Any;

struct A {}
struct B {}

fn f<T: 'static>(param: Vec<T>) {
    if let Some(_param_a) = (&param as &dyn Any).downcast_ref::<Vec<A>>() {
        println!("I am a Vec<A>");
    }
    else {
        println!("I am something else");
    }
}

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

这没有任何运行时成本。

您正在寻找的东西在当前的 Rust 中还相当可用,但一旦专业化功能登陆就会可用。使用专业化,并可在当前夜间进行测试,您的函数将如下所示:

#![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

它甚至可以通过提供类似“演员表”的特征使其变得通用:

trait Cast<To> {
    fn with_cast(&self, fun: impl FnOnce(Option<&To>));
}

impl<T, To> Cast<To> for T {
    default fn with_cast(&self, fun: impl FnOnce(Option<&To>)) {
        fun(None)
    }
}

使用 Castf 函数将如下所示:

fn f<T>(param: Vec<T>) {
    impl Cast<Vec<A>> for Vec<A> {
        fn with_cast(&self, fun: impl FnOnce(Option<&Self>)) {
            fun(Some(self));
        }
    }
    param.with_cast(|val: Option<&Vec<A>>| {
        if let Some(_param_a) = val {
            // here I can access fields of param_a
            println!("I'm a Vec<A>");
        } else {
            println!("I'm something else");
        }
    });
    // ...
}

Playground