简体   繁体   English

Rust 为任何类型定义特征接口?

[英]Rust define trait interface for any type?

I am building a rendering engine, one thing I want is to handle mangagement of arbitrary mesh data, regardless of representation.我正在构建一个渲染引擎,我想要的一件事是处理任意网格数据的管理,而不管表示如何。

My idea was, define a trait the enforces a function signature and then serialization can be handled by the user while I handle all the gpu stuff.我的想法是,定义一个特征来强制执行 function 签名,然后在我处理所有 gpu 的东西时,用户可以处理序列化。 This was the trait I made:这是我创造的特质:

pub enum GpuAttributeData
{
    OwnedData(Vec<Vec<i8>>, Vec<u32>),
}
pub trait GpuSerializable
{
    fn serialize(&self) -> GpuAttributeData;
}

So very simple, give me a couple of arrays.很简单,给我几个arrays。

When i tested things inside the crate it worked, but I moved my example outside the crate, ie I have this snippet in an example:当我在板条箱内测试东西时它起作用了,但是我将我的示例移到了板条箱之外,即我在示例中有这个片段:


impl <const N : usize> GpuSerializable for [Vertex; N]
{
    fn serialize(&self) -> GpuAttributeData
    {
        let size = size_of::<Vertex>() * self.len();
        let data = unsafe {
            let mut data = Vec::<i8>::with_capacity(size);
            copy_nonoverlapping(
                self.as_ptr() as *const i8, data.as_ptr() as *mut i8, size);
            data.set_len(size);
            data
        };

        // let indices : Vec<usize> = (0..self.len()).into_iter().collect();
        let indices = vec![0, 1, 2];
        let mut buffers :Vec<Vec<i8>> = Vec::new();
        buffers.push(data);

        return GpuAttributeData::OwnedData(buffers, indices);
    }
}

Which gives me this error:这给了我这个错误:

error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
  --> examples/01_spinning_triangle/main.rs:41:1
   |
41 | impl <const N : usize> GpuSerializable for [Vertex; N]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------
   | |                                          |
   | |                                          this is not defined in the current crate because arrays are always foreign
   | impl doesn't use only types from inside the current crate
   |
   = note: define and implement a trait or new type instead

This utterly breaks my design.这完全破坏了我的设计。 The whole point of what I am trying to achieve is, anyone anywhere should be able to implement that trait, inside or outside any crate, to be able to serialize whatever data they have, in whatever esoteric format it is.我想要实现的全部目的是,任何地方的任何人都应该能够在任何箱子内部或外部实现该特征,以便能够以任何深奥的格式序列化他们拥有的任何数据。

Can I somehow bypass this restriction?我能以某种方式绕过这个限制吗? If not, is there another way I could enforce at runtime "give me an object that has this function signature among its methods"?如果没有,是否有另一种方法可以在运行时强制执行“给我一个 object,它的方法中有这个 function 签名”?

Rust has the orphan rule , which says that any implementation of a trait on a type must exist in the same crate as at least one of the trait or the type. Rust 有孤儿规则,它表示对类型的特征的任何实现必须与特征或类型中的至少一个存在于同一个板条箱中。 In other words, both types can't be foreign.换句话说,这两种类型都不能是外来的。 (It's a bit more complex than this -- for example, you can implement a foreign generic trait on a foreign type if a generic type argument to the generic trait is a type declared in the local crate.) (它比这更复杂一些——例如,如果泛型特征的泛型类型参数是在本地板条箱中声明的类型,您可以在外来类型上实现外来泛型特征。)

This is why you frequently see so-called "newtypes" in Rust, which are typically unit structs with a single member, whose sole purpose is to implement a foreign trait on a foreign type.这就是为什么您经常在 Rust 中看到所谓的“新类型”,它们通常是具有单个成员的单元结构,其唯一目的是在外来类型上实现外来特征。 The newtype lives in the same crate as the implementation, so the compiler accepts the implementation. newtype 与实现在同一个板条箱中,因此编译器接受实现。

This could be realized in your example with a newtype around the array type:这可以在您的示例中使用围绕数组类型的新类型来实现:

#[repr(transparent)]
struct VertexArray<const N: usize>(pub [Vertex; N]);

impl<const N: usize> Deref for VertexArray<N> {
    type Target = [Vertex; N];
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<const N: usize> DerefMut for VertexArray<N> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl<const N: usize> GpuSerializable for VertexArray<N> {
    // ...
}

Note that because of #[repr(transparent)] , the layout of both VertexArray<N> and [Vertex; N]请注意,由于#[repr(transparent)]VertexArray<N>[Vertex; N] [Vertex; N] are guaranteed to be identical, meaning you can transmute between them, or references to them. [Vertex; N]保证是相同的,这意味着您可以在它们之间转换,或引用它们。 This allows you to, for example, reborrow a &[Vertex; N]例如,这允许您重新借用&[Vertex; N] &[Vertex; N] as a &VertexArray<N> safely, which means you can store all of your data as [Vertex; N] &[Vertex; N]作为&VertexArray<N>安全,这意味着您可以将所有数据存储为[Vertex; N] [Vertex; N] and borrow it as the newtype at zero cost whenever you need to interact with something expecting an implementation of GpuSerializable . [Vertex; N]并在您需要与期望GpuSerializable实现的事物进行交互时以零成本将其作为新类型借用。

impl<const N: usize> AsRef<VertexArray<N>> for [Vertex; N] {
    fn as_ref(&self) -> &VertexArray<N> {
        unsafe { std::mem::transmute(self) }
    }
}

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

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