How to define a user-defined trait that behaves likes that Copy imposes requirements to the implementation?

If one type wants to implement Copy, the Copy trait would impose the requirements that all of its fields of that type have implemented Copy. Currently, we seem cannot user-define such a trait that imposes similar requirements to its implementation.

trait deep-check A{}

struct B{
    v: i32
}
impl A for B{} // it would be an error if `i32` didn't implement `A`

To answer the question: you can't. Support for Copy is deeply baked into the compiler.

In addition, arguably by design, in general traits shouldn't affect items that are outside the purview of the current impl Trait for Type item. Which is to say, such an impl should only be allowed to affect the semantics of Type values, but not the definition (i.e. struct or enum item) of either Type or Trait. Allowing that (which is what you seem to be asking for).

What might help though is an attribute macro, or maybe even a derive macro. When placed on a struct or enum, it can inspect the definition of that type definition and generate code based on that. So you could generate an impl block for the type with the bounds you want.

attribute macro is unhelpful if the impl type is in the other crate.

trait deep-check A{}
impl A for xxx::Type{  // we want to check whether the contained type in `Type` implements A
}

The other main option is to do something like Send: make the trait unsafe and require the user to assert that all fields are valid when implementing it. You could then potentially write a derive that checks that for the user. One example of doing this is bytemucks traits + derives, e.g. AnyBitPattern in bytemuck - Rust.

2 Likes

Since it is a common usage, why do we not add this function to the core language?

Because it still has undecided design problems. See Tracking Issue for auto traits (auto_traits) -- formerly called opt-in built-in traits (optin_builtin_traits) · Issue #13231 · rust-lang/rust · GitHub

3 Likes

Yes, but they are a bit different. auto trait will be automatically implemented for the type if these types therein satisfy the trait. However, Copy is not an auto trait, this issue is, when we manually implement the trait for a particular type, the prerequires are these types therein shall satisfy that trait.

Hmm I think I get what you mean now. I think the usage is not common enough to justify the inclusion. The only time I find this helpful is writing ffi-safe struct and hope there's a trait to ensure I did not break ffi safety.

The big problem with auto traits is ecosystem impact. There's no realistic way for people to opt out of every random auto trait that other people invent.

Thus traits in the ecosystem are generally better as ordinary traits, with a derive that does the right thing. Getting everyone to integrate with them is more annoying, but at least it's not unsound.

Traits provide consumers with an abstraction, such that they do not care for the details of the implementation other than that it meets the expected contract.

What do I, as a consumer of your trait, care whether the fields of your implementing struct also implement the trait? Either those fields are private and I don't care about them at all, or they are public and their type is known to me (together with their trait implementations, if any).

Copy is different in that the compiler is its consumer: when a type is Copy, the compiler may treat it differently—and for that to be sound, the compiler ensures that all fields are also Copy.

I guess I'm struggling to see how this could be useful. Perhaps if you could set out a concrete use case...?

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.