top | item 44498376

(no title)

jhugo | 7 months ago

> 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.

discuss

order

chrismorgan|7 months ago

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

It’s not about that, it’s about type system power as the article said. In former days there was no way to express the constraint; but these days you can <https://play.rust-lang.org/?gist=d1947d81a126df84f3c91fb29b5...>:

  impl<T> Clone for WrapArc<T>
  where
      Arc<T>: Clone,
  {
      …
  }

jhugo|7 months ago

Yeah. My bad. I got annoyed by the "is broken" terminology of TFA and wasn't thinking clearly :/

almostdeadguy|7 months ago

All the #[derive(Clone)] does is generate a trait impl of Clone for the struct, which itself can be bounded by trait constraints. It doesn't have to know that every use of the struct ensures generic parameters have to/don't have to be Clone. It doesn't have to make guarantees about how the struct is used at all.

It only needs to provide constraints that must hold for it to call clone() on each field of the struct (i.e. the constraints that must hold for the generated implementation of the fn clone(&self) method to be valid, which might not hold for all T, in which case a Struct<T> will not implement Clone). The issue this post discusses exists because there are structs like Arc<T> that are cloneable despite T not being Clone itself [1]. In a case like that it may not be desirable to put a T: Clone constraint on the trait impl, because that unnecessarily limits T where Struct<T>: Clone.

[1]: https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Clon...

enricozb|7 months ago

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`.

jhugo|7 months ago

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.

berkes|7 months ago

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

Why not?

jhugo|7 months ago

Types don't carry behavioral information about what the method does internally. Everything about a method is known from its signature.

The compiler doesn't introspect the code inside the method and add additional hidden information to its signature (and it would be difficult to reason about a compiler that did).