我正在为C API编写一个Rust包装器。它包含一个可能失败的函数,在这种情况下,它返回一个编码为int
的错误代码。我们称它们为SOME_ERROR
和OTHER_ERROR
,它们的值分别是1和2。我想编写一个包含这些错误代码的enum,如下所示:
// Declared in a seperate C header
const SOME_ERROR: c_int = 1;
const OTHER_ERROR: c_int = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
enum ErrorCodeWrapper {
SomeError = SOME_ERROR,
OtherError = OTHER_ERROR,
}
这是我的第一个问题。似乎不可能将std::os::raw::c_int
指定为枚举的基础类型。但我确实觉得它应该是,因为int
不需要是32位宽。有什么办法可以做到这一点吗?然后我想要一些方法转换为原始错误代码:
use std::os::raw::c_int;
impl ErrorCodeWrapper {
fn from_raw(raw: c_int) -> Option<Self> {
match raw {
SOME_ERROR => Some(Self::SomeError),
OTHER_ERROR => Some(Self::OtherError),
_ => None
}
}
unsafe fn from_raw_unchecked(raw: c_int) -> Self {
*(&raw as *const _ as *const Self)
}
fn as_raw(self) -> c_int {
unsafe { *(&self as *const _ as *const c_int) }
}
}
我能找到的唯一方法是&;bit-cast&;c_int
to和fromErrorCodeWrapper
是c风格的,通过转换指针然后解引用它。这应该作为ErrorCodeWrapper
和int
有相同的大小和对齐,和每个ErrorCodeWrapper
变量的值映射到相应的错误代码。然而,这个解决方案对我来说有点粗俗;有没有一个更习惯的,像c++的std::bit_cast
?
此外,是否有可能将ErrorCodeWrapper::from_raw
中的match
语句替换为简单的有效性检查,以便在更多变体的情况下简化代码?
最后一位代码,必要的错误实现:
use std::{fmt::Display, error::Error};
impl Display for ErrorCodeWrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
Self::SomeError => "some error",
Self::OtherError => "some other error",
})
}
}
impl Error for ErrorCodeWrapper {}
现在让我们想象第二个包装器SuperErrorCodeWrapper
,它包含ErrorCodeWrapper
的一些或所有变体,具有相同的描述和所有内容。这就意味着:
可以"factor out";将
ErrorCodeWrapper
和SuperErrorCodeWrapper
的常见变体放入一个单独的enum中。ErrorCodeWrapper
和SuperErrorCodeWrapper
则有一个包含此枚举的变体。然而,我并不喜欢这种嵌套,当专注于一个特定的错误时,它似乎是任意的。在两个枚举中复制公共变量。
后者会给现有的样板文件增加很多内容。一个宏可以是一个可行的选择来处理这个问题吗?
是否有一个库可以为我处理所有这些?
我的第一个问题是。将
std::os::raw::c_int
指定为枚举的基础类型似乎是不可能的。但我确实觉得它应该是,因为int
不需要是32位宽。有什么办法可以做到这一点吗?
。有一个RFC 2016年(我甚至不能访问RFC文本。它似乎被删除了),但它是关闭的:
我们在@rust-lang/lang会议上进行了讨论,并决定尽管RFC动机良好,但它没有充分解决必须克服的各种实现复杂性,也没有充分解决与卫生的交互。在考虑此RFC之前,扩展属性系统以支持更通用的路径是有意义的(但这是一项重要的工作)。
你能做的最好的是在所有配置中使用#[cfg_attr]
。c_int
在这里定义为,所有当前选项都是
#[cfg_attr(any(target_arch = "avr", target_arch = "msp430"), repr(i16))]
#[cfg_attr(not(any(target_arch = "avr", target_arch = "msp430")), repr(i32))]
enum ErrorCodeWrapper { ... }
有没有更习惯的,像c++的
std::bit_cast
?
是的;std::mem::transmute()
.
可以"factor out";将
ErrorCodeWrapper
和SuperErrorCodeWrapper
的常见变体放入一个单独的enum中。ErrorCodeWrapper
和SuperErrorCodeWrapper
则有一个包含该enum的变体。
如果你这样做,你失去了transmute()
的能力(或指针强制转换,没关系),因为它们将不再与int
布局兼容。
一个宏可以是一个可行的选择来处理这个?
可能是的。
是否有一个库可以为我处理所有这些?
我不知道处理所有这些的所有库(尽管可能存在一个),但是有thiserror
(和朋友)用于Error
和Display
实现,strum::FromRepr
可以帮助您处理from_raw()
。