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

Opt-in builtin traits, take 2: default and negative impls. #127

Merged
merged 5 commits into from
Sep 18, 2014

Conversation

nikomatsakis
Copy link
Contributor

The high-level idea is to add language features that simultaneously
achieve three goals:

  1. move Send and Share out of the language entirely and into the
    standard library, providing mechanisms for end users to easily
    implement and use similar "marker" traits of their own devising;
  2. make "normal" Rust types sendable and sharable by default, without
    the need for explicit opt-in; and,
  3. continue to require "unsafe" Rust types (those that manipulate
    unsafe pointers or implement special abstractions) to "opt-in" to
    sendability and sharability with an unsafe declaration.

@nikomatsakis
Copy link
Contributor Author

cc @flaper87

@nikomatsakis
Copy link
Contributor Author

cc @pnkfelix -- I'd specifically like feedback re: the GC discussion.

@bstrie
Copy link
Contributor

bstrie commented Jun 18, 2014

Can you imagine any other potential use of unsafe trait other than for default traits?

@nikomatsakis
Copy link
Contributor Author

@bstrie I think so, yes.

On the one hand, any trait with methods can declare those methods as unsafe, but that's not quite the same thing -- that's saying that the methods cannot themselves check all the conditions that would be required to show they are safe (and hence the caller must be relied upon to do so).

I imagine you would want to use an unsafe trait to signal that the method is running in an unsafe environment where it is expected not to make use of certain facilities, even though we can't stop it from doing so, or where it is expected to maintain other invariants. For example, imagine a trait Traceable that ran during the GC and was used to trace through smart pointers. That would probably be an unsafe trait because the code within will run asynchronously with respect to the main thread and thus it cannot touch any ref cells or locks without fear of inducing deadlock. It may also be expected not to fail or to uphold other similar "above the call of duty" invariants.

To summarize, the role of the unsafe keyword depends on context:

  • on a fn or method, it signals "extra conditions are present on the caller"
  • on a block, it signals "this block must prove extra conditions"
  • on an trait, it would signal "extra conditions are present on implementors"
  • on an impl, it would signal "this impl must prove extra conditions"

You can use unsafe on both a trait and the methods in a trait to signal extra conditions both ways. :)

@pnkfelix
Copy link
Member

I think the RFC needs to elaborate on the interaction of this feature with trait objects (i.e. bounds).

In particular: I assume that something here is going to automatically become a candidate for being listed as a bound on a trait bound (or a closure), to fit with the current usage of Send as a trait bound when necessary.

The question is: Is it unsafe traits that become such bounds? Or traits with a default impl?

@nikomatsakis
Copy link
Contributor Author

@pnkfelix Yes, a good point! I thought of that as I was traveling home last night. I was making the assumption that we would permit arbitrary traits to be listed as bounds, but of course that merits another RFC, and for the time being I'd probably be inclined to limit "extra" bounds on trait objects to traits with no methods, so as to avoid having to codegen the vtables.

@LeoTestard
Copy link
Contributor

Couldn't this be extended to the Copy trait too ? Something like

unsafe trait Copy { }

impl Copy for .. { }

// disallow shallow copy for types with destructors. add more negative
// impls here if needed, I don't know what our exact rules for being Copy
// are at the moment
impl<T: Drop> !Copy for T { }

Of course, Copy should still be understood by the compiler, but this would allow to override current rules with unsafe impl. Maybe it would also make changing the rules for being Copy easier. I have absolutely no idea whether this would be a good idea or not though, just something that came to my mind, and I'm curious to know what you think about it.

@lilyball
Copy link
Contributor

@LeoTestard I don't think it makes sense for Copy, because a type that is inferred to not be Copy cannot have Copy "safety" added back in later. By that I mean that if a type isn't Copy because the type of one of its fields isn't, there is no way for that type to somehow make itself safe to memcpy after all, so there is no situation in which it makes sense for a type to ever opt back in to Copy.


We add a notion of a *default impl*, written:

impl Trait for .. { }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe _ is better? impl Trait for _ {}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the motivation is via analogy with the .. wildcard when matching fields of a struct: .. stands for any set of values except for the ones listed explicitly, i.e.:

fn main() {
    struct S { x: i8, y: i8, z: i8 };
    let s = S { x: 1, y: 2, z: 3 };
    match s {
        S { x, .. } => println!("x: {}", x)
    }
}

Because of the precedent illustrated above, I think .. denotes well the notions that

  1. many implementations are being introduced by this impl, and
  2. there may be exceptions to this impl.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_ means "any (other) value/type omitting it's name here".
Rust already uses it in let and match statements in variant ways:

let p = box 1;
let _ = p;
let (x, _) = (Some(1), Some(2));
match x {
  Some(_) => { }
  _ => { }
}

I think _ denotes well the notions 1 and 2 from @pnkfelix above, and
3. _ is shorter than ...
4. .. was already used in rangex..y array[..N] and slice[a..b] syntax, in other significations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does _ denote notion 2? It accepts anything, not "anything except ..."

the fact that one can use it as a catch-all in match after a series of other clauses seems like more an artifact of the semantics of match rather than inherent to _ itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Thu, Sep 18, 2014 at 02:15:26AM -0700, Liigo Zhuang wrote:

_ means "any (other) value/type omitting it's name here".

The key point is that impl Trait for Foo { } is very different from
applying an impl Trait for .. { } to Foo -- the latter walks down
recursively, applying a test to the parameter types, and the former
does not. _ does not imply this recursive walk to me.

@brendanzab
Copy link
Member

From a cursory look, this seems like a great solution!

@pcwalton pcwalton merged commit 2860ed8 into rust-lang:master Sep 18, 2014
pnkfelix added a commit to pnkfelix/rfcs that referenced this pull request Oct 8, 2014
In particular:

* The RFC associated with rust-lang#127 should have had a link to rust-lang#19 as well
  (and has been assigned RFC rust-lang#19); it also was revised to match the
  markdown href style of other RFCs.

* RFC rust-lang#34 needed its header entries filled in,

* RFC rust-lang#123 had a typo in its header, and

* RC rust-lang#155 was revised to match the markdown href style of other RFCs.
bors added a commit to rust-lang/rust that referenced this pull request Jan 5, 2015
…tsakis

This commit introduces the syntax for negative implementations of traits
as shown below:

`impl !Trait for Type {}`

cc #13231
Part of RFC rust-lang/rfcs#127

r? @nikomatsakis
@keean
Copy link

keean commented Apr 21, 2016

Hope it's not too late to join the conversation. I think Opt-out traits seem to have the same problems as negative trait bounds, for example:

trait NoDisplay {}
impl NoDisplay for .. {}
impl<T> !NoDisplay for T where T : Display {}
fn foo<T: Display>(data: T) { panic!("{}", data) }
fn foo<T: NoDisplay>(_: T) { println!("Can't display") }

@Centril Centril added A-traits Trait system related proposals & ideas A-unsafe Unsafe related proposals & ideas A-typesystem Type system related proposals & ideas A-sync Synchronization related proposals & ideas labels Nov 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-sync Synchronization related proposals & ideas A-traits Trait system related proposals & ideas A-typesystem Type system related proposals & ideas A-unsafe Unsafe related proposals & ideas
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants