是否可以使用类型来访问 Rust 联合的特定字段?

Is it possible to use a type to access a specific field of a Rust union?

作为将 C 接口映射到 Rust 的一部分,我想处理一个联合,它直接存储了一些本机类型,并有一个指向所有其他类型的分配类型的指针。

如何围绕可以 select 并根据包装类型参数使用适当字段的联合实现参数化包装类型?

在这种情况下,我想添加一个直接读取数据结构的 Rust 包装器,而不是先将其转换为 Rust 原生类型的解决方案。添加其他支持类型来“欺骗”编译器是可以的。最终目标是能够编写类似于这样的代码:

let the_list: List<i32> = get_a_list();
for i in the_list {
    println!("value")
}

我跳过 IntoIterator 和朋友的定义,因为这归结为根据类型访问正确的字段。

该代码非常不安全,但我们可以假设用户为类型参数提供了正确的类型。

这个问题还有其他解决方案,例如为每种类型提供显式 reader 函数,但本文的重点是了解是否有办法在没有这些且不引入过度开销的情况下使其工作。

代码不起作用,但说明了我想要完成的事情:

#![feature(specialization)]
use std::convert::From;

union Data<T> {
    int_value: i64,
    ptr_value: *const T,
}

default impl<T> From<&Data<T>> for &T {
    fn from(data: &Data<T>) -> &T {
        &*data.ptr_value
    }
}

impl From<&Data<i64>> for &i64 {
    fn from(data: &Data<i64>) -> &i64 {
        &*data.int_value
    }
}

fn show<T>(value: &T) {
    println!("value: {}", value);
}

fn main() {
    let value = String::from("magic");
    let data: Data<&str> = Data {
        ptr_value: value.as_ptr(),
    };
    show(data.into());
}

我已将示例最小化以避免讨论其他方面。这会产生以下错误:

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
  --> examples/union_convert.rs:10:14
   |
10 | default impl<T> From<&Data<T>> for &T {
   |              ^ type parameter `T` must be used as the type parameter for some local type
   |
   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
   = note: only traits defined in the current crate can be implemented for a type parameter

我已经尝试在联合周围添加一个包装器来处理类型双关,但这似乎只是将错误消息推来推去。也可以返回 &T 以外的其他类型,但我不明白如何使其正常运行。使用不同的 return 类型也是一种选择,但它仍然归结为 select 根据类型选择正确的字段。

查看 std::vec::Vec 的实现,它做了类似的事情,但在这种情况下,它 总是 将类型的内存表示映射到真实类型。在这种情况下,我想 select 基于写入值时使用的类型的正确联合字段。

其他没有真正回答这个问题的类似问题:

更新: 提供了一个更独特的例子,使用 into() 转换字段之一的联合类型。

我会定义自己的特征而不是 From 以避免与标准库实现冲突。我还会定义一个新的包装器/标记类型。这消除了在通用点中存储特定类型之一时发生冲突的可能性。

struct Other<T>(T);

union Data<T> {
    bool_value: bool,
    int_value: i64,
    ptr_value: *const T,
}

trait Access<T> {
    type Output;

    unsafe fn access(&self) -> &Self::Output;
}

impl<T> Access<Other<T>> for Data<T> {
    type Output = T;

    unsafe fn access(&self) -> &Self::Output {
        &*self.ptr_value
    }
}

impl<T> Access<bool> for Data<T> {
    type Output = bool;

    unsafe fn access(&self) -> &Self::Output {
        &self.bool_value
    }
}

impl<T> Access<i64> for Data<T> {
    type Output = i64;

    unsafe fn access(&self) -> &Self::Output {
        &self.int_value
    }
}

fn main() {
    let value = 123_f64;
    let data: Data<f64> = Data { ptr_value: &value };

    // This is safe because we just created this with
    // a `f64` and nothing happened in-between.
    unsafe {
        println!("{}", Access::<Other<f64>>::access(&data));
    }
}

另请参阅: