访问枚举变体的价值

发布于 2025-01-28 04:48:06 字数 4131 浏览 3 评论 0原文

我正在使用 arnoflow noreferrer“> arrayfire-rust-rust crate )。

ArrayFire具有一个键入的struct array a>代表矩阵。所有可接受的类型实现hasafenum hasafenum。该特征具有许多相关类型,其值对于实现此特征的类型而言并不相同。

由于我需要在rwlock中使用安全性语言互动中的数组的引用,因此我定义了以下结构:

pub struct ExAfRef(pub RwLock<ExAfArray>);

impl ExAfRef {
    pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
        Self(RwLock::new(ExAfArray::new(slice, dim, dtype)))
    }

    pub fn value(&self) -> ExAfArray {
        match self.0.try_read() {
            Ok(refer) => (*refer),
            Err(_) => unreachable!(),
        }
    }
}

由struct:struct:

pub struct ExAf {
    pub resource: ResourceArc<ExAfRef>,
}

impl ExAf {
    pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
        Self {
            resource: ResourceArc::new(ExAfRef::new(slice, dim, dtype)),
        }
    }

    // This function is broken
    pub fn af_value<T: HasAfEnum>(&self) -> &Array<T> {
        self.resource.value().value()
    }
}

在以下枚举的帮助下:

pub enum ExAfArray {
    U8(Array<u8>),
    S32(Array<i32>),
    S64(Array<i64>),
    F32(Array<f32>),
    F64(Array<f64>),
}

impl ExAfArray {
    pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
        let array = Array::new(slice, dim);

        match dtype {
            ExAfDType::U8 => ExAfArray::U8(array),
            ExAfDType::S32 => ExAfArray::S32(array.cast::<i32>()),
            ExAfDType::S64 => ExAfArray::S64(array.cast::<i64>()),
            ExAfDType::F32 => ExAfArray::F32(array.cast::<f32>()),
            ExAfDType::F64 => ExAfArray::F64(array.cast::<f64>()),
        }
    }

    // This function is broken
    pub fn value<T: HasAfEnum>(&self) -> &Array<T> {
        // match self {
        //     ExAfArray::U8(array) => array,
        //     ExAfArray::S32(array) => array,
        //     ExAfArray::S64(array) => array,
        //     ExAfArray::F32(array) => array,
        //     ExAfArray::F64(array) => array,
        // }

        if let ExAfArray::U8(array) = self {
            return array;
        } else if let ExAfArray::S32(array) = self {
            return array;
        } else if let ExAfArray::S64(array) = self {
            return array;
        } else if let ExAfArray::F32(array) = self {
            return array;
        } else {
            let ExAfArray::F64(array) = self;
            return array;
        }
    }

    pub fn get_type(&self) -> ExAfDType {
        match self {
            ExAfArray::U8(array) => ExAfDType::U8,
            ExAfArray::S32(array) => ExAfDType::S32,
            ExAfArray::S64(array) => ExAfDType::S64,
            ExAfArray::F32(array) => ExAfDType::F32,
            ExAfArray::F64(array) => ExAfDType::F64,
        }
    }
}

我使用了枚举因为我的语言Interop“框架”不支持通用结构,并且因为hasafenum性状具有关联的类型(因此,使用dyn的动态调度是不可行的(至少对我的知识))。

这在初始化新阵列方面效果很好。

但是,当我需要在数组上应用一些操作时,我需要能够访问枚举变体存储的值。但是,我无法编写一个类型签名以访问该值的函数,因为动态调度不可用,仿制药太好了。

由于所有变体都是元组,我是否可以使用内置枚举功能访问元组变体的值?

edit

我正在使用 rustler

I am working on some language bindings to Arrayfire using the arrayfire-rust crate.

Arrayfire has a typed struct Array<T> which represents a matrix. All acceptable types implement the HasAfEnum trait. This trait has a number of associated types, whose values are not the same for the types that implement this trait.

Since I need a reference to the array in a Rwlock for safe language interop, I have defined the following struct:

pub struct ExAfRef(pub RwLock<ExAfArray>);

impl ExAfRef {
    pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
        Self(RwLock::new(ExAfArray::new(slice, dim, dtype)))
    }

    pub fn value(&self) -> ExAfArray {
        match self.0.try_read() {
            Ok(refer) => (*refer),
            Err(_) => unreachable!(),
        }
    }
}

which is contained by a struct:

pub struct ExAf {
    pub resource: ResourceArc<ExAfRef>,
}

impl ExAf {
    pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
        Self {
            resource: ResourceArc::new(ExAfRef::new(slice, dim, dtype)),
        }
    }

    // This function is broken
    pub fn af_value<T: HasAfEnum>(&self) -> &Array<T> {
        self.resource.value().value()
    }
}

With the help of the following enum:

pub enum ExAfArray {
    U8(Array<u8>),
    S32(Array<i32>),
    S64(Array<i64>),
    F32(Array<f32>),
    F64(Array<f64>),
}

impl ExAfArray {
    pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
        let array = Array::new(slice, dim);

        match dtype {
            ExAfDType::U8 => ExAfArray::U8(array),
            ExAfDType::S32 => ExAfArray::S32(array.cast::<i32>()),
            ExAfDType::S64 => ExAfArray::S64(array.cast::<i64>()),
            ExAfDType::F32 => ExAfArray::F32(array.cast::<f32>()),
            ExAfDType::F64 => ExAfArray::F64(array.cast::<f64>()),
        }
    }

    // This function is broken
    pub fn value<T: HasAfEnum>(&self) -> &Array<T> {
        // match self {
        //     ExAfArray::U8(array) => array,
        //     ExAfArray::S32(array) => array,
        //     ExAfArray::S64(array) => array,
        //     ExAfArray::F32(array) => array,
        //     ExAfArray::F64(array) => array,
        // }

        if let ExAfArray::U8(array) = self {
            return array;
        } else if let ExAfArray::S32(array) = self {
            return array;
        } else if let ExAfArray::S64(array) = self {
            return array;
        } else if let ExAfArray::F32(array) = self {
            return array;
        } else {
            let ExAfArray::F64(array) = self;
            return array;
        }
    }

    pub fn get_type(&self) -> ExAfDType {
        match self {
            ExAfArray::U8(array) => ExAfDType::U8,
            ExAfArray::S32(array) => ExAfDType::S32,
            ExAfArray::S64(array) => ExAfDType::S64,
            ExAfArray::F32(array) => ExAfDType::F32,
            ExAfArray::F64(array) => ExAfDType::F64,
        }
    }
}

I have used an enum because generic structs are not supported in my language-interop "framework" and because the HasAfEnum trait has associated types (hence dynamic dispatch using dyn is not viable (at least to my knowledge)).

This has worked fine for initializing new arrays.

However when I need to apply some operation on an array, I need to be able to access the value stored by the enum variant. However I am unable to write a type signature for a function to access the value, as dynamic dispatch is not usable and generics are too boilerplate.

Since all variants are tuples, is there some way I can access the value of the tuple variant using a built-in enum feature?

EDIT:

I am using rustler

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鯉魚旗 2025-02-04 04:48:06

简而言之,没有一种方法可以做您目前在Rust中尝试做的事情。

您的功能被打破了,因为您试图正交使用仿制药与它们的工作方式。当在Rust中调用通用函数时,调用者填充类型参数,而不是Callee。但是,从某种意义上说,您的枚举“知道”了混凝土数组类型是什么,因此只有它才能确定该类型的参数应该是什么。如果此不匹配阻止您的进度,则通常需要重新考虑您的代码结构。

这也解释了为什么没有内置的枚举方法可以执行您要做的事情。该方法将与您的value方法陷入同一问题。当您要检查Rust中的枚举内容时,需要在其上进行模式匹配。

至少有一种方法可以尝试实现您的目标,但我不会真正推荐它。使代码更接近可行的一种更改是将闭合插入函数以进行修改(以下语法当前不是有效的生锈,但它会跨越想法):

pub fn modify<'a, F>(&'a self, op: F)
where
    F: for<T: HasAfEnum> FnOnce(&'a Array<T>)
{
    // This looks repetitive, but the idea is that in each branch
    // the type parameter T takes on the appropriate type for the variant
    match self {
        ExAfArray::U8(array) => op(array),
        ExAfArray::S32(array) => op(array),
        ExAfArray::S64(array) => op(array),
        ExAfArray::F32(array) => op(array),
        ExAfArray::F64(array) => op(array),
    }
}

不幸的是,for&lt; t&gt; fntrait(t)语法尚不存在,我什至不确定是否有提议可以添加它。这可以通过宏来解决:

pub(crate) fn call_unary<F, T, U>(arg: T, f: F) -> U
where F: FnOnce(T) -> U {
    f(arg)
}

macro_rules! modify {
    ($ex_af_array:expr, $op:expr) => {
        match &$ex_af_array {
            ExAfArray::U8(array) => call_unary(array, $op),
            ExAfArray::S32(array) => call_unary(array, $op),
            ExAfArray::S64(array) => call_unary(array, $op),
            ExAfArray::F32(array) => call_unary(array, $op),
            ExAfArray::F64(array) => call_unary(array, $op),
        }
    };
}

call_unary助手需要确保键入推理正常工作。 ($ op)(array)当需要推断为$ op的参数类型时,将无法编译。

现在,该解决方案主要涵盖for&lt; t&gt; Fntrait(t)将提供,但这不是很干净的代码(尤其是在宏观体内进行消毒之后),如果宏被滥用,编译器错误将很差。

In short, no there is not a way to do what you seem to be trying to do in Rust presently.

Your functions are broken because you are trying to use generics orthogonally to how they work. When a generic function is called in Rust, the caller fills in the type parameters, not the callee. However, your enum in a sense "knows" what the concrete array type is, so only it can determine what that type parameter is supposed to be. If this mismatch is blocking your progress, this usually calls for a reconsideration of your code structure.

This also explains why there is no built-in enum method that does what you're trying to do. That method would run into the same issue as your value method. When you want to inspect the contents of an enum in Rust, you need to pattern match on it.

There is at least one way to try to accomplish your goal, but I would not really recommend it. One change that makes the code closer to being viable is by passing a closure into the function to make the modification, (the syntax below is not currently valid Rust but it gets the idea across):

pub fn modify<'a, F>(&'a self, op: F)
where
    F: for<T: HasAfEnum> FnOnce(&'a Array<T>)
{
    // This looks repetitive, but the idea is that in each branch
    // the type parameter T takes on the appropriate type for the variant
    match self {
        ExAfArray::U8(array) => op(array),
        ExAfArray::S32(array) => op(array),
        ExAfArray::S64(array) => op(array),
        ExAfArray::F32(array) => op(array),
        ExAfArray::F64(array) => op(array),
    }
}

Unfortunately the for<T> FnTrait(T) syntax does not exist yet and I'm not even sure if there's a proposal for it to be added. This can be worked around through a macro:

pub(crate) fn call_unary<F, T, U>(arg: T, f: F) -> U
where F: FnOnce(T) -> U {
    f(arg)
}

macro_rules! modify {
    ($ex_af_array:expr, $op:expr) => {
        match &$ex_af_array {
            ExAfArray::U8(array) => call_unary(array, $op),
            ExAfArray::S32(array) => call_unary(array, $op),
            ExAfArray::S64(array) => call_unary(array, $op),
            ExAfArray::F32(array) => call_unary(array, $op),
            ExAfArray::F64(array) => call_unary(array, $op),
        }
    };
}

The call_unary helper is needed to ensure type inference works properly. ($op)(array) will fail to compile when the types of the arguments to $op need to be inferred.

Now this solution mostly covers the functionality that for<T> FnTrait(T) would provide, but it's not very clean code (especially after the macro body is sanitized), and the compiler errors will be poor if the macro is misused.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文