Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Default but only for tests?

Tags:

rust

traits

I want to provide default values for structs to be used only within tests (and not accidentally in production). I thought that I could make the defaults opt-in by defining my own trait TestDefault and implement Default for any type that implements it. Then, one could access all the features of the standard Default trait using something like this

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

let test_struct = TestStruct::default();

To clarify, I want to implement a foreign trait on local type, which should be allowed, but with an artificial layer of indirection to make it opt-in.

I've tried

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

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

where the compiler complains that error[E0782]: trait objects must include the 'dyn' keyword, inserting it instead causes it to fail because error[E0038]: the trait 'TestDefault' cannot be made into an object.

Then I tried

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

and got

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

which probably hints at the actual error, but I don't entirely understand it. Is there any way to do this? Or get opt-in default some other way?

like image 642
Sebastian Holmin Avatar asked Nov 28 '25 17:11

Sebastian Holmin


1 Answers

You can use the #[cfg(test)] annotation to only enable the Default implementation when testing:

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`
like image 87
Finomnis Avatar answered Dec 01 '25 14:12

Finomnis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!