Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avoid having two distinct systems for copying #17884

Closed
thestinger opened this issue Oct 9, 2014 · 11 comments
Closed

avoid having two distinct systems for copying #17884

thestinger opened this issue Oct 9, 2014 · 11 comments
Labels
A-typesystem Area: The type system P-medium Medium priority

Comments

@thestinger
Copy link
Contributor

Since Copy doesn't result in an automatic Clone implementation, there are two entirely separate systems for doing copies. There are many types providing Copy without a corresponding Clone implementation and nothing enforces that Clone performs the same operation.

Ideally, the trait system could allow an implementation of Clone for T: Copy without introducing problematic conflicts. If that's not possible, then something else should be done to provide a system that's at least as sane as the story in C++.

@thestinger thestinger added A-typesystem Area: The type system I-nominated labels Oct 9, 2014
@Gankra
Copy link
Contributor

Gankra commented Oct 9, 2014

This should be fine to do when some of the upgraded coherence stuff lands. The only issue is if there was a type that was Copy that wanted a non-trivial implementation of Clone. I don't expect that to be the case. It would probably also be very poor design if T::clone() had different behavior from a copy when T: Copy. Sort of like making Add and Sub do completely different things.

@arthurprs
Copy link
Contributor

that's a good point
sub

@pcwalton
Copy link
Contributor

pcwalton commented Oct 9, 2014

I don't believe we should block 1.0 on this.

@pnkfelix
Copy link
Member

pnkfelix commented Oct 9, 2014

Assigning P-high, not 1.0. (Team collectively agreed that we can debate how to resolve this, but it should not block 1.0.)

@pnkfelix pnkfelix added P-medium Medium priority and removed I-nominated labels Oct 9, 2014
@nikomatsakis
Copy link
Contributor

The upgraded coherence stuff would allow a blanket impl like

impl<T:Copy> Clone for T { ... }

However, this has a bad interaction with tuple impls, because you would need an impl like:

impl<T, U> Clone for (T, U)
    where T:Clone, U:Clone, (T,U):!Copy { ... }

Without negative bounds, we can't write such an impl. We could imagine adding those though.

The other option would be permitting specialization, in which case the tuple impl could be given higher priority than the blanket impl -- however, that has the negative affect that others can override as well.

Regarding triage this strikes me as a wart but one we could live with for 1.0.

@bstrie
Copy link
Contributor

bstrie commented Oct 10, 2014

Having Copy imply Clone seems like a clear consistency and usability win.

If the inability to implement Clone directly on tuples is the only problem, then while it's certainly a wart then I don't think it will be a problem in practice (use a struct instead!).

In any case...

Without negative bounds, we can't write such an impl.

...aren't negative bounds a part of https://github.com/rust-lang/rfcs/blob/master/active/0003-opt-in-builtin-traits.md?

@zwarich
Copy link

zwarich commented Oct 10, 2014

...aren't negative bounds a part of https://github.com/rust-lang/rfcs/blob/master/active/0003-opt-in-builtin-traits.md?

I think that RFC only includes negative impls, meaning that a type can opt out of a default trait.

@nikomatsakis
Copy link
Contributor

On Fri, Oct 10, 2014 at 08:48:42AM -0700, Ben Striegel wrote:

If the inability to implement Clone directly on tuples is the only problem, then while it's certainly a wart then I don't think it will be a problem in practice (use a struct instead!).

It's not the only problem the same thing applies to Option etc. i.e., you'd have to have:

impl<T:Clone> Clone for Option<T>
    where T:!Copy
{ ... }

@glaebhoerl
Copy link
Contributor

It might be worth observing that in this case, in a sense, the compiler is being over-conservative: it would actually be OK to allow all of impl<T: Copy> Clone for T, impl<T: Copy> Copy for Option<T>, and impl<T: Clone> Clone for Option<T> (with the obvious definitions) at the same time, the compiler just doesn't (and probably can't) know that. These definitions are confluent. No matter how you derive a Clone impl for Option<T> (given T: Copy), the result will be the same. If you go through the first impl, it will Copy the whole Option as-is; with the third impl, it will go variant-wise and Copy the contained value. But it doesn't make any difference in the end, and so doesn't actually violate coherence. (Likewise for the other analogous cases, e.g. tuples.)

A much harder question is whether this insight can be useful in any way. In a dependently typed language, maybe the compiler could reason about the definitions of the impls, prove confluence, and so accept them. But definitely not in Rust. Possibly we could shift the proof burden onto the programmer, and allow her to say, "These definitions are confluent. Believe me.". But how, and where? Which impl (or perhaps trait?) do you attach an annotation to, and with what content? These impls aren't supposed to "know about" each other, and it would feel kind of hackish and improper for them to somehow refer to each other directly when stating confluence.

(GHC's type-level functions (type familys, a.k.a. associated types) have this no-overlap-except-if-confluent rule, but being at the type level, GHC gets to check whether the property holds directly.)

@steveklabnik
Copy link
Member

Talking with @nikomatsakis on IRC, it would seem that there are enough complications with this that we do not plan on doing it. An RFC might be able to resolve them, but probably not. If anyone feels super strongly about this, please persue one.

@bstrie
Copy link
Contributor

bstrie commented Mar 26, 2015

I would like to hear more about these complications, given that this is still a pain point. Even if we don't want a blanket impl, I'm curious if simple making #[derive(Copy)] implicitly also imply #[derive(Clone)] would solve most of the problems here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-typesystem Area: The type system P-medium Medium priority
Projects
None yet
Development

No branches or pull requests

10 participants