【问题标题】:How to implement Default but only for tests?如何实现默认但仅用于测试?
【发布时间】:2022-09-26 11:01:30
【问题描述】:

我想为仅在测试中使用的结构提供默认值(而不是在生产中意外使用)。我认为我可以通过定义我自己的特征 TestDefault 并为实现它的任何类型实现 Default 来选择默认值。然后,可以使用类似这样的东西访问标准Default trait 的所有功能

use TestDefault; // TestStruct (defined in my crate) implements TestDefault, thus also Default

let test_struct = TestStruct::default();

澄清一下,我想在本地类型上实现一个外部特征,这应该是允许的,但是有一个人为的间接层来让它选择加入。

我试过了

pub trait TestDefault {
    fn test_default() -> Self;
}

impl Default for TestDefault {
    fn default() -> Self {
        Self::test_default()
    }
}

编译器抱怨error[E0782]: trait objects must include the \'dyn\' keyword,插入它反而会导致它失败,因为error[E0038]: the trait \'TestDefault\' cannot be made into an object。

然后我尝试了

impl<T> Default for T
where
    T: TestDefault,
{
    fn default() -> T {
        T::test_default()
    }
}

并得到

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
   --> src/lib.rs:158:14
    |
158 |         impl<T> Default for T
    |              ^ type parameter `T` must be used as the type parameter for some local type
    |
    = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter

这可能暗示了实际的错误,但我并不完全理解。有没有办法做到这一点?或者以其他方式获得选择加入默认值?

  • @ChayimFriedman 谢谢,但我不这么认为。 TestStruct 将在我的 crate 中定义。所以我仍然会在应该允许的本地类型上实现一个外部特征,但是有一个人为的间接层。
  • 那为什么不直接实现Default呢?无论如何,如果你使用泛型,你就可以为任何类型实现它,而不仅仅是那些在你的 crate 中定义的类型。
  • 要点是您只能在手动编写use TestDefault 时访问默认实现,因为它只是测试的便利功能,而不是实际的默认设置。我从extension traits 的工作方式中获得灵感,您必须通过use 他们才能访问相关的方法。但它不会是通用的,因为我必须为我的每个结构手动实现TestDefault。
  • 即使您想要的东西被允许,情况也不会如此 - 只要Default 本身存在,Default 实现将始终可用。

标签: rust traits


【解决方案1】:

测试时可以使用#[cfg(test)]注解只启用Default实现:

struct MyStruct {
    data: u32,
}

#[cfg(test)]
impl Default for MyStruct {
    fn default() -> Self {
        Self { data: 42 }
    }
}

fn main() {
    let s = MyStruct::default(); // FAILS
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let s = MyStruct::default(); // WORKS
        assert_eq!(s.data, 42);
    }
}
> cargo test
running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
> cargo run
error[E0599]: no function or associated item named `default` found for struct `MyStruct` in the current scope
  --> src/main.rs:13:23
   |
1  | struct MyStruct {
   | --------------- function or associated item `default` not found for this
...
13 |     let s = MyStruct::default(); // FAILS
   |                       ^^^^^^^ function or associated item not found in `MyStruct`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `default`, perhaps you need to implement it:
           candidate #1: `Default`

【讨论】:

  • 我不知道你可以在impl 块上使用#[cfg(test)]!对于我的具体问题,这实际上是一个非常好的解决方案,谢谢!
  • 这就是它的作用,它根据是否是测试运行启用/禁用代码编译:)
猜你喜欢
  • 2010-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-29
相关资源
最近更新 更多