我知道有无数类似的问题关于这个错误消息。但是我还没有找到我的错误的答案。首先,请允许我解释一下我的问题。这是编译器错误
error[E0277]: the size for values of type `(dyn std::error::Error + 'static)` cannot be known at compilation time
--> src/category.rs:117:47
|
117 | "Time" => get_input::<Time>(help_msg, get_value::<Time>(default_value))?.to_string(),
| ----------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
| |
| required by a bound introduced by this call
|
= help: the trait `Sized` is not implemented for `(dyn std::error::Error + 'static)`
= help: the trait `std::error::Error` is implemented for `Box<T>`
= note: required for `Box<(dyn std::error::Error + 'static)>` to implement `std::error::Error`
重要的函数签名有以下两个。
pub fn get_input<T: FromStr>(msg: &str, default_value: Option<T>) -> Result<T, Box<dyn Error>>
where
T::Err: Error + 'static,
T: std::fmt::Display,
和
fn get_value<T: FromStr>(value_str: &str) -> Option<T>
where
T::Err: Error + 'static,
据我所知,这个错误源于这样一个事实,即在编译时无法知道错误类型,我推测是因为它使用String
类型作为错误消息?无论哪种方式,之所以如此令人困惑,是因为代码错误只针对自定义结构Time
,而不针对其他内置程序
let value: String = match type_str {
"u32" => get_input::<u32>(help_msg, get_value::<u32>(default_value))?.to_string(),
"i32" => get_input::<i32>(help_msg, get_value::<i32>(default_value))?.to_string(),
"f32" => get_input::<f32>(help_msg, get_value::<f32>(default_value))?.to_string(),
"bool" => get_input::<bool>(help_msg, get_value::<bool>(default_value))?.to_string(),
"String" => get_input::<String>(help_msg, get_value::<String>(default_value))?.to_string(),
// This is the line that gives the compiler error
"Time" => get_input::<Time>(help_msg, get_value::<Time>(default_value))?.to_string(),
_ => "None".to_string(),
};
为了完成,这里是我定义的结构,我假设我必须改变错误类型,但我不知道是什么。
use std::error::Error;
pub struct Time {
hour: u8,
minute: u8,
}
impl Time {
pub fn new(hour: u8, minute: u8) -> Time {
Time { hour, minute }
}
}
impl std::str::FromStr for Time {
type Err = Box<dyn Error + 'static>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split(':').collect();
if parts.len() == 2 {
let hour: u8 = parts[0].parse()?;
let minute: u8 = parts[1].parse()?;
Ok(Time::new(hour, minute))
} else {
Err("Invalid time format".into())
}
}
}
impl std::fmt::Display for Time {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:02}:{:02}", self.hour, self.minute)
}
}
我知道我在这里是代码转储,但我真的不知道如何解决这个问题。当然也有变通的办法,但到了这一点,我真的很想知道为什么这段代码不起作用,以及如何解决这个问题。
任何帮助都是非常感谢的。
2条答案
按热度按时间p4rjhz4m1#
问题
在这里,
T::Err: Error
需要Box<dyn Error>
来实现Error
,这很直观。正如this answer解释的那样,对于Box
艾德Error
来实现Error
,它需要是Sized
,哪个trait对象(即dyn Error
)不是。为什么返回
Box<dyn Error>
首先如此方便?因为它允许您使用?
运算符来冒泡几乎任何类型的错误。脱糖到
注意
e.into()
。?
尝试将您得到的错误转换为您想要返回的错误,并且有一个blanket impl,大致如下所示:这使得任何实现
Error
的类型都可以实现。手动解决方案
@alagner的答案当然是正确的,但是从C++继承了一些你在Rust中并不真正需要的东西,并省略了Rust提供的一些细节。您可以使用一个新类型定义一个更简单的 Package 器:
现在,您可以创建一个新的错误,如下所示:
TimeError("Wrong length".into())
,它就能正常工作了。这样做的问题是,现在我们不能再使用
?
操作符了,因为Into<TimeError>
没有实现任何其他我们可能想要冒泡的错误。如果我们能简单地写上但不幸的是,Rust的一致性规则不允许我们这样做。
Error
是为TimeError
实现的,使得blanket impl包括impl From<TimeError> for TimeError
,但是该impl已经存在(因为From<T>
是为每个T
实现的)。如果你仍然想使用?
,你必须为你想传播的每个E
手动实现From<E> for TimeError
:如果你最终需要传播一大堆不同的错误(在这个特定的情况下似乎不太可能,但无论如何),你可能想使用一个小的trait技巧来概括这一点。你可以定义你自己的
Error
的super trait,而TimeError
* 没有 * 实现,为任何你想转换成TimeError
的E
实现它,然后你可以为任何实现该trait的类型覆盖implFrom
:现在,如果你需要能够传播另一种错误
E
,你只需要添加impl ConvertibleError for E {}
和?
将再次工作,不需要显式地写入impl From<E> for TimeError
。或者,你可以写一个简单的宏来为你编写
From
impl:并称之为像
便捷的解决方案
anyhow
crate(其他错误处理库也可用)提供了自己的Error
类型,其工作方式与Box<dyn Error>
非常相似。它允许您将可能遇到的几乎所有错误冒泡出来,并提供了一个方便的anyhow::anyhow!
宏来创建临时错误。旁白
你的
get_value
函数除了将Result
转换为Option
之外,基本上什么也不做,标准库已经有了一个方法:Result::ok
:wn9m85ua2#
我不确定这是不是惯用的(可能不是),但你可以做的是创建自定义的错误类型,包含一个错误类型的Box,比如说像C++中的“私有实现”。inner和out都必须实现Error trait。这样你就可以将外部调用委托给内部调用: