使用包含在 Option 枚举中的 trait-bounded 参数创建函数时出错
Error in creating a function with a trait-bounded parameter wrapped in Option enum
我想为我的 MyError
枚举(其变体是我程序中的不同错误类型)创建一个方法,returns 一个 String
值描述了给定的 MyError
变体。例如:
pub enum MyError {
Error1,
Error2,
}
impl MyError {
pub fn to_str(&self) -> String {
match self {
Error1 => format!("Error1: bla bla bla"),
Error2 => format!("Error2: na na na"),
}
}
}
一切都很好,但问题是,我有一个新的错误变体(比如 Error3
),它必须在方法中将参数传递给它的 format!()
宏,就像这样:
Error3 => format!("la la la {:?}", arg),
这个参数可以是任何类型,只要它可以派生出 Debug
特征。所以我的解决方案是
pub enum MyError {
Error1,
Error2,
Error3
}
impl MyError {
pub fn to_str(&self, arg: Option<&impl fmt::Debug>) -> String {
match self {
Error1 => format!("bla bla bla"),
Error2 => format!("na na na"),
Error3 => format!("la la la {:?}", arg),
}
}
}
我在 Option
中包装了 trait-bounded 参数,因为 MyError
的某些变体不需要它(例如 Error1
)。这适用于 Error3
变体,我可以在没有任何编译错误的情况下执行以下操作:
eprintln!("{}", MyError::Error3.to_str(Some(vec![1, 2, 3])));
它打印 Error3
的相关错误消息。但是当我尝试将该方法用于不需要额外参数的其他变体时,例如打电话
eprintln!("{}", MyError::Error1.to_str(None));
它returns下面的编译错误:
type annotations needed
cannot infer type for type parameter `impl fmt::Debug` declared on the associated function `to_str`rustc(E0282)
为什么编译器不能在这里推断出 None
的类型?
您需要指定具体类型,即使是 None
。像这样:
let n: Option<&Vec<usize>> = None;
eprintln!("{}", MyError::Error1.to_str(n));
即使函数(方法)只指定了特征边界,而不是具体类型,编译器需要确切地知道调用者传递给该函数的每个值的类型。
当然,您可以使用不同类型的值调用该函数(但每个类型都需要清楚并且必须遵守特征界限)。一个例子:
pub fn main() {
let v1 = vec![1, 2, 3];
let v2 = "A str here";
eprintln!("{}", MyError::Error3.to_str(Some(&v1)));
eprintln!("{}", MyError::Error3.to_str(Some(&v2)));
let n: Option<&Vec<usize>> = None;
eprintln!("{}", MyError::Error1.to_str(n));
let n2: Option<&Vec<String>> = None;
eprintln!("{}", MyError::Error1.to_str(n2));
}
您可以了解更多信息here。
它无法推断出这一点的原因可能是由于一些烦人的边缘情况。主要的是确定传递的枚举的大小。枚举类型的大小取决于其最大变体的大小。你可以用 assert_neq(std::mem::size_of::<Option<i32>>(), std::mem::size_of::<Option<i8>>());
看到这个。在不知道它包含什么具体类型的情况下,我们无法知道函数在传递参数时应该使用多少space。
最简单的方法可能是允许用户将具体类型指定为类型参数。即使您传入 None
,您仍然可以指定具体类型作为替代(例如:error.to_str::<()>(None)
)。
impl MyError {
pub fn to_str<D: fmt::Debug>(&self, arg: Option<&D>) -> String {
// etc.
}
}
否则,我建议分离方法以完全避免该问题。
impl MyError {
pub fn to_str(&self) -> String {
match self {
Error1 => format!("bla bla bla"),
Error2 => format!("na na na"),
Error3 => format!("la la la"),
}
}
pub fn to_str_with_context(&self, arg: &impl fmt::Debug) -> String {
match self {
Error1 => format!("bla bla bla"),
Error2 => format!("na na na"),
Error3 => format!("la la la {:?}", arg),
}
}
}
除此之外,我只能提供一般编码技巧。
我的第一直觉是您可能会发现使用显示包装器更方便。
pub struct MyErrorWithContext<'a, D> {
err: &'a MyError,
context: Option<&'a D>,
}
impl<'a, D: fmt::Debug> fmt::Display for MyErrorWithContext<'a, D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.err {
Error1 => write!(f, "bla bla bla"),
Error2 => write!(f, "na na na"),
Error3 => write!(f, "la la la {:?}", &self.context),
}
}
}
impl MyError {
pub fn display<D: Debug>(&self, context: Option<&D>) -> MyErrorWithContext<D> {
MyErrorWithContext {
err: self,
context,
}
}
}
println!("Error: {}", error.display(Some(&foo)));
我想为我的 MyError
枚举(其变体是我程序中的不同错误类型)创建一个方法,returns 一个 String
值描述了给定的 MyError
变体。例如:
pub enum MyError {
Error1,
Error2,
}
impl MyError {
pub fn to_str(&self) -> String {
match self {
Error1 => format!("Error1: bla bla bla"),
Error2 => format!("Error2: na na na"),
}
}
}
一切都很好,但问题是,我有一个新的错误变体(比如 Error3
),它必须在方法中将参数传递给它的 format!()
宏,就像这样:
Error3 => format!("la la la {:?}", arg),
这个参数可以是任何类型,只要它可以派生出 Debug
特征。所以我的解决方案是
pub enum MyError {
Error1,
Error2,
Error3
}
impl MyError {
pub fn to_str(&self, arg: Option<&impl fmt::Debug>) -> String {
match self {
Error1 => format!("bla bla bla"),
Error2 => format!("na na na"),
Error3 => format!("la la la {:?}", arg),
}
}
}
我在 Option
中包装了 trait-bounded 参数,因为 MyError
的某些变体不需要它(例如 Error1
)。这适用于 Error3
变体,我可以在没有任何编译错误的情况下执行以下操作:
eprintln!("{}", MyError::Error3.to_str(Some(vec![1, 2, 3])));
它打印 Error3
的相关错误消息。但是当我尝试将该方法用于不需要额外参数的其他变体时,例如打电话
eprintln!("{}", MyError::Error1.to_str(None));
它returns下面的编译错误:
type annotations needed
cannot infer type for type parameter `impl fmt::Debug` declared on the associated function `to_str`rustc(E0282)
为什么编译器不能在这里推断出 None
的类型?
您需要指定具体类型,即使是 None
。像这样:
let n: Option<&Vec<usize>> = None;
eprintln!("{}", MyError::Error1.to_str(n));
即使函数(方法)只指定了特征边界,而不是具体类型,编译器需要确切地知道调用者传递给该函数的每个值的类型。
当然,您可以使用不同类型的值调用该函数(但每个类型都需要清楚并且必须遵守特征界限)。一个例子:
pub fn main() {
let v1 = vec![1, 2, 3];
let v2 = "A str here";
eprintln!("{}", MyError::Error3.to_str(Some(&v1)));
eprintln!("{}", MyError::Error3.to_str(Some(&v2)));
let n: Option<&Vec<usize>> = None;
eprintln!("{}", MyError::Error1.to_str(n));
let n2: Option<&Vec<String>> = None;
eprintln!("{}", MyError::Error1.to_str(n2));
}
您可以了解更多信息here。
它无法推断出这一点的原因可能是由于一些烦人的边缘情况。主要的是确定传递的枚举的大小。枚举类型的大小取决于其最大变体的大小。你可以用 assert_neq(std::mem::size_of::<Option<i32>>(), std::mem::size_of::<Option<i8>>());
看到这个。在不知道它包含什么具体类型的情况下,我们无法知道函数在传递参数时应该使用多少space。
最简单的方法可能是允许用户将具体类型指定为类型参数。即使您传入 None
,您仍然可以指定具体类型作为替代(例如:error.to_str::<()>(None)
)。
impl MyError {
pub fn to_str<D: fmt::Debug>(&self, arg: Option<&D>) -> String {
// etc.
}
}
否则,我建议分离方法以完全避免该问题。
impl MyError {
pub fn to_str(&self) -> String {
match self {
Error1 => format!("bla bla bla"),
Error2 => format!("na na na"),
Error3 => format!("la la la"),
}
}
pub fn to_str_with_context(&self, arg: &impl fmt::Debug) -> String {
match self {
Error1 => format!("bla bla bla"),
Error2 => format!("na na na"),
Error3 => format!("la la la {:?}", arg),
}
}
}
除此之外,我只能提供一般编码技巧。 我的第一直觉是您可能会发现使用显示包装器更方便。
pub struct MyErrorWithContext<'a, D> {
err: &'a MyError,
context: Option<&'a D>,
}
impl<'a, D: fmt::Debug> fmt::Display for MyErrorWithContext<'a, D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.err {
Error1 => write!(f, "bla bla bla"),
Error2 => write!(f, "na na na"),
Error3 => write!(f, "la la la {:?}", &self.context),
}
}
}
impl MyError {
pub fn display<D: Debug>(&self, context: Option<&D>) -> MyErrorWithContext<D> {
MyErrorWithContext {
err: self,
context,
}
}
}
println!("Error: {}", error.display(Some(&foo)));