←back to thread

128 points RGBCube | 4 comments | | HN request time: 0.247s | source
Show context
jhugo ◴[] No.44498376[source]
> we cannot just require all generic parameters to be Clone, as we cannot assume they are used in such a way that requires them to be cloned.

No, this is backwards. We have to require all generic parameters are Clone, as we cannot assume that any are not used in a way that requires them to be Clone.

> The reason this is the way it is is probably because Rust's type system wasn't powerful enough for this to be implemented back in the pre-1.0 days. Or it was just a simple oversight that got stabilized.

The type system can't know whether you call `T::clone()` in a method somewhere.

replies(4): >>44498401 #>>44498749 #>>44499040 #>>44499325 #
enricozb ◴[] No.44498749[source]
For structs, why couldn't rust check the necessary bounds on `T` for each field to be cloned? E.g. in

    #[derive(Clone)]
    struct Weird<T> {
      ptr: Arc<T>,
      tup: (T, usize)
    }

for `ptr`, `Arc<T>: Clone` exists with no bound on `T`. But for `tup`, `(T, usize): Clone` requires `T: Clone`.

Same thing for other derives, such as `Default`.

replies(1): >>44498755 #
1. jhugo ◴[] No.44498755[source]
Because it doesn't know if you're relying on T being Clone in method bodies. The internal behavior of methods is not encoded in the type system.
replies(3): >>44498847 #>>44499243 #>>44508105 #
2. RGBCube ◴[] No.44498847[source]
What?

The way the derives work is they generate code by utilizing the fields and their types. Here is a trivial implementation (of a custom trait, rather than Clone. still holds though) which will prove you wrong:

<https://github.com/cull-os/carcass/blob/master/dup%2Fmacros%...>

3. saghm ◴[] No.44499243[source]
You can already write method bodies today that have constraints that aren't enforced by the type definition though; it's trivially possible to write a method that requires Debug on a parameter without the type itself implementing Debug[0], for example. It's often even encouraged to define the constraints on impl blocks rather than the type definition. The standard library itself goes out of its way to define types in a way that allow only partial usage due to some of their methods having bounds that aren't enforced on the type definition. Rust's hashmap definition in the standard library somewhat notably doesn't actually enforce that the type of the key is possible to hash, which allows a hashmap of arbitrary types to be created but not inserted into unless the value actually implements Hash[1].

[0]: https://play.rust-lang.org/?version=stable&mode=debug&editio...

[1]: https://www.reddit.com/r/rust/comments/101wzdq/why_rust_has_...

4. enricozb ◴[] No.44508105[source]
I'm not sure how these two things are related. When writing an `impl` for a struct, there's no assumption on the bounds of the generics (if any) unless they are specified _at the impl site_.

For example, the bounds of T in

    impl<T> Weird<T> {
      ..
    }
are independent of the bounds of `T` in any other impl or the struct definition.

Unless I'm missing something...