提问人:Benjamin Philip 提问时间:5/12/2022 最后编辑:Benjamin Philip 更新时间:5/13/2022 访问量:1819
访问枚举变体的值
Access the value of an enum variant
问:
我正在使用 arrayfire-rust crate 对 Arrayfire 进行一些语言绑定。
Arrayfire 有一个类型化结构 Array<T>
它表示一个矩阵。所有可接受的类型都实现 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 中调用泛型函数时,调用者填写类型参数,而不是被调用者。但是,从某种意义上说,您的枚举“知道”具体数组类型是什么,因此只有它才能确定该类型参数应该是什么。如果这种不匹配阻碍了您的进度,这通常需要重新考虑您的代码结构。
这也解释了为什么没有内置的枚举方法可以执行您尝试执行的操作。该方法会遇到与您的方法相同的问题。当你想在 Rust 中检查一个枚举的内容时,你需要对它进行模式匹配。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<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)(array)
$op
现在,此解决方案主要涵盖了将提供的功能,但它不是非常干净的代码(尤其是在宏体被清理之后),如果宏被滥用,编译器错误将很差。for<T> FnTrait(T)
评论
F: for<T: HasAfEnum> FnOnce(&'a Array<T>)
:导致错误:在此上下文中只能使用生存期参数:-/。
评论