繁体   English   中英

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

[英]Cast a struct with a generic type parameter to a specific type

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

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

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

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

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

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

在 Rust 中,是否有一种简单直接的方法来实现这一点,而无需牺牲性能,也无需更改函数签名或在函数外部编写任何代码(因此没有枚举包装器、特征、动态/运行时“魔术” “, 等等。)。

如果您能够约束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());
}

操场

TL; 博士

使用特征来表示T应该具有的行为:

struct Answer {}
struct Question {}
trait Summarizable { fn summarize(&self); }
impl Summarizable for Answer {
    fn summarize(&self) { println!("I'm an answer"); }
}
impl Summarizable for Question {
    fn summarize(&self) { println!("I'm a question"); }
}

fn main() {
    let posts: Vec<&dyn Summarizable> = vec![&Answer {}, &Question {}];
    for post in posts {
        post.summarize();
    }
}

我相信 Rust 与其他语言有很大的不同,至少在 OOP 方面如此,所以我们在其他语言中的常见反应最好被抛在后面。 我从 Rust 中了解到的部分精神是,我们几乎从不以“我的价值观实际包含什么”模式运作。

Rust 并没有尝试去猜测值的类型,而是精心制作了构造,将这种猜测从我们的手中转移到代码的结构中。

例如,让我们考虑match构造,以Book 中的示例为例:

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

非常简单的,但这里的关键思想是:在比赛的所有武器,我们确切地知道什么类型我们在行动上 我相信这些特殊的信息可以帮助我解决手头的问题,即根据值的类型尝试不同的行为,因为它提示我挑战我编写的依赖于这种未知状态的代码值。

我没有理由在想知道我的价值观是什么时编码,因为语言结构允许我在我知道的上下文中进行编写。 在这种情况下,它通过从手动类型检查中移动代码来工作:

fn main() {
    let posts: Vec<&dyn Summarizable> = vec![&Answer {}, &Question {}];
    for post in posts {
        // this is pseudocode
        if typeof post == Answer {
            println!("I'm an answer");
        }
        if typeof post == Question {
            println!("I'm a question");
        }
    }
}

特定于对应于该类型的行为的代码:

impl Summarizable for Answer {
    fn summarize(&self) { println!("I'm an answer"); }
}
impl Summarizable for Question {
    fn summarize(&self) { println!("I'm a question"); }
}

在使实际的主要功能方式更清楚实际应该发生什么的同时,也就是总结帖子,不要怀疑它们是什么:

fn main() {
    let posts: Vec<&dyn Summarizable> = vec![&Answer {}, &Question {}];
    for post in posts {
        post.summarize();
    }
}

ps 我忽略了你问题的“无特征”部分,因为我觉得它不合理。 如果你想写其他语言,他们可以! 但是 Rust 中的 trait 是解决一般“根据值的类型采取不同行动”问题的惯用方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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