如何为C错误代码编写包装枚举?



我正在为C API编写一个Rust包装器。它包含一个可能失败的函数,在这种情况下,它返回一个编码为int的错误代码。我们称它们为SOME_ERROROTHER_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_intto和fromErrorCodeWrapper是c风格的,通过转换指针然后解引用它。这应该作为ErrorCodeWrapperint有相同的大小和对齐,和每个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";将ErrorCodeWrapperSuperErrorCodeWrapper的常见变体放入一个单独的enum中。ErrorCodeWrapperSuperErrorCodeWrapper则有一个包含此枚举的变体。然而,我并不喜欢这种嵌套,当专注于一个特定的错误时,它似乎是任意的。

  • 在两个枚举中复制公共变量。

后者会给现有的样板文件增加很多内容。一个宏可以是一个可行的选择来处理这个问题吗?

是否有一个库可以为我处理所有这些?

我的第一个问题是。将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";将ErrorCodeWrapperSuperErrorCodeWrapper的常见变体放入一个单独的enum中。ErrorCodeWrapperSuperErrorCodeWrapper则有一个包含该enum的变体。

如果你这样做,你失去了transmute()的能力(或指针强制转换,没关系),因为它们将不再与int布局兼容。

一个宏可以是一个可行的选择来处理这个?

可能是的。

是否有一个库可以为我处理所有这些?

我不知道处理所有这些的所有库(尽管可能存在一个),但是有thiserror(和朋友)用于ErrorDisplay实现,strum::FromRepr可以帮助您处理from_raw()