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?
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`
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With