访问枚举变体的值

Access the value of an enum variant

我正在使用 arrayfire-rust crate.

对 Arrayfire 进行一些语言绑定

Arrayfire 具有类型结构 Array<T> which represents a matrix. All acceptable types implement the 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!(),
        }
    }
}

它包含在结构中:

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,
        }
    }
}

我使用了枚举,因为通用结构在我的语言互操作“框架”中不受支持,并且因为 HasAfEnum 特性具有关联类型(因此使用 dyn 的动态调度不可行(至少据我所知))。

这对于初始化新数组效果很好。

然而,当我需要对数组应用一些操作时,我需要能够访问枚举变量存储的值。但是我无法为函数编写类型签名来访问该值,因为动态调度不可用并且泛型太样板了。

由于所有变体都是元组,有什么方法可以使用内置枚举功能访问元组变体的值吗?

编辑:

我正在使用 rustler

简而言之,不,没有办法做你目前似乎在 Rust 中尝试做的事情。

您的函数已损坏,因为您正试图以与它们的工作方式正交的方式使用泛型。当在 Rust 中调用泛型函数时,调用者填写类型参数,而不是被调用者。但是,您的枚举在某种意义上“知道”具体数组类型是什么,因此只有它可以确定该类型参数应该是什么。如果这种不匹配阻碍了您的进步,这通常需要重新考虑您的代码结构。

这也解释了为什么没有 built-in 枚举方法来完成您想要做的事情。该方法将 运行 与您的 value 方法陷入相同的问题。当你想在 Rust 中检查枚举的内容时,你需要对其进行模式匹配。

至少有一种方法可以尝试实现您的目标,但我不会真正推荐它。使代码更接近可行的一个更改是将闭包传递到函数中以进行修改,(下面的语法目前不是有效的 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<T> 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 的参数类型需要推断时,($op)(array) 将无法编译。

现在这个解决方案主要涵盖了 for<T> FnTrait(T) 将提供的功能,但它不是很干净的代码(尤其是在清理宏体之后),如果宏被误用,编译器错误会很糟糕。