当前位置: 首页 > 知识库问答 >
问题:

是否有任何方法可以转换为泛型T,其中T是任意的特征类型?

杨经武
2023-03-14

我的问题标题有点模糊,但本质上我想实现以下几点:

  • struct Foo实现行为A和行为B和行为C
  • 结构栏实现行为A
  • Foo和Bar都实现了一些Content特性
  • 从一个

从广义上讲,这是可能的,通过强制Content明确地了解行为A等,如:

fn as_a(&self) -> Option<&BehaviourA>

但是,这要求内容类型明确了解所有行为类型,如果内容作为api导出到板条箱中,这是不可能的。

理想情况下,内容将具有如下api:

fn behaviour<T>(&self) -> Option<T> where T: Any;

可在运行时调用,如下所示:

match content_item.behaviour::<&BehaviourA>() {
  Some(x) => { ... },
  None => {}
}

但是,我找不到任何方法来实现此功能。

我最好的尝试是这样的:

fn behaviour<T>(&self) -> Option<T> where T: Any {
  let own_type = TypeId::of::<T>();
  if own_type == TypeId::of::<Behaviour>() { return Some(self as T); }
  return None;
}

但这会导致:

src/foo.rs:21:62: 21:71 error: non-scalar cast: `&interface::test::Foo` as `T`
src/foo.rs:21       if own_type == TypeId::of::<Behaviour>() { return Some(self as T); }

我知道没有办法在运行时内省对象的特性列表,但这里我显式地将其转换为实现中的已知类型;很简单,界T并不总是满足的。

有什么方法可以做到这一点,即使是不安全的?

显示错误的playpen示例如下:http://is.gd/fZNFA8

(注意,我已经看到你能将泛型限制为T,其中T是Self实现的特征吗?),但我的问题不同;简而言之,有没有任何方法,甚至不安全地在运行时生成特征引用?)


共有1个答案

太叔岳
2023-03-14

这里的根本问题是,特质不是类型。当你说

可悲的是,从1.6开始,仍然没有办法做到这一点。

我所知道的使类似的东西能够工作的唯一方法是显式的a-la COM:您需要一个基本接口,它允许您请求“转换到X”的引用,然后在每个具体类型上为所有支持的X实现它。

如果你愿意涉足不安全、混乱的代码,你可以通过一些宏(游戏笔)得到你想要的东西:

// Requires nightly compiler, since it depends on implementation details.
#![feature(raw)]

use std::any::TypeId;
use std::mem::transmute;
use std::raw::TraitObject;

macro_rules! qi {
    ($e:expr, $t:ty) => {
        unsafe {
            let iid = TypeId::of::<$t>();
            let ptr = &$e;
            match ptr.query_interface(&iid) {
                Some(to) => {
                    let tmp: &$t = transmute(to);
                    Some(tmp)
                },
                None => None,
            }
        }
    };
}

macro_rules! impl_unknown_for {
    ($base:ty: $($ints:ty),*) => {
        unsafe impl Unknown for $base {
            fn query_interface(&self, iid: &TypeId) -> Option<TraitObject> {
                unsafe {
                    $(
                        if *iid == TypeId::of::<$ints>() {
                            let to = transmute::<&$ints, _>(self);
                            return Some(to);
                        }
                    )*
                    None
                }
            }
        }
    };
}

unsafe trait Unknown {
    fn query_interface(&self, iid: &TypeId) -> Option<TraitObject>;
}

trait BehaviourA { fn a(&self); }
trait BehaviourB { fn b(&self); }
trait BehaviourC { fn c(&self); }

struct Thingy;

impl_unknown_for!(Thingy: BehaviourA, BehaviourC);

impl BehaviourA for Thingy {
    fn a(&self) { println!("Thingy::a"); }
}

impl BehaviourC for Thingy {
    fn c(&self) { println!("Thingy::c"); }
}

fn main() {
    let obj: Box<Unknown> = Box::new(Thingy);
    if let Some(ba) = qi!(obj, BehaviourA) { ba.a(); }
    if let Some(bb) = qi!(obj, BehaviourB) { bb.b(); }
    if let Some(bc) = qi!(obj, BehaviourC) { bc.c(); }
}

 类似资料:
  • 目前,我使用这个解决方案,使用,而不是:

  • 问题内容: 编辑:我改变了一些例子,以获得想法: 喜欢 …而无需创建公共接口并为Integer和Float创建子类来实现它 如果没有,类似这样的东西可能会更有意义并且有用 如果呢?是一个通配符,为什么我们不应该限制某些类型? 问题答案: 在非常极端的情况下(没有的Java 7之前的版本),我也希望能够做到这一点。例如 不管实际的类型是什么,这都允许我打电话。换句话说,将包含所有提供的类型的“ AP

  • 问题内容: 请帮助我获得一个where 本身就是一个泛型类型。如我现在所见,Spring RestTemplate现在不支持此功能。我正在使用Spring MVC版本3.1.2 这是我要使用的代码:代码: 我收到此错误: 这是明显的错误,但是今天如何解决呢? 比我想得到我的通用响应类型: 现在,我使用此解决方案,并且不使用: 问题答案: 这是一个已知问题。现在,通过引入修复了此问题,您可以显式 继

  • 我使用的是Azure.data.tables nuget包的12.0.0-beta.6。当我尝试调用TableClient.GetQueryAsync时,它会给出错误: “类型”T“必须是引用类型,才能将其用作泛型类型或方法”TableClient.GetEntityAsync(string,string,IEnumerable,CancellationToken)“中的参数”T“。” 我看不出我

  • 问题内容: 我最近读了《 Go编程语言》 ,这是学习golang编程语言的好资源。在6.2节中有一段关于类型的副本实例在方法中是否为指针接收者的段落,我无法理解。有没有什么可以用有意义的例子来解释本段的? 6.2使用指针接收器的方法 如果所有命名类型T的方法本身都具有接收者T类型(不是* T),则可以安全地复制该类型的实例;调用其任何方法必然会产生一个副本。例如,time.Duration值被自由

  • 问题内容: 为什么以下代码会编译?该方法返回该类型或其子类的实例。类中的代码调用该方法。编译器允许将返回值存储到类型的变量(显然不在的层次结构中)。 即使在擦除类型之后, 返回类型也不应该仍然是的实例 吗? 该方法的字节码为: 编辑:一致地替换为。 问题答案: 这实际上是合法的类型推断*。 我们可以将其简化为以下示例(Ideone): 因为是接口,所以允许编译器推断(无意义的,实际上是)交集类型。