1

I'd like to define a trait which forces its implementors to under no circumstances be sent to, or shared between, threads. It should suffice to mark the trait as !Send, but Rust doesn't seem to let me.

Is it possible?

Example (playground):

#![feature(optin_builtin_traits)]

// This is a syntax error
//trait ThreadThing : !Send {}

// This doesn't work either
trait ThreadThing { }
impl !Send for ThreadThing {}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Edd Barrett
  • 3,425
  • 2
  • 29
  • 48
  • 2
    To what end would this be useful? Just don't send things between threads if you don't want them to be. – Shepmaster May 23 '18 at 15:25
  • It would be nice to have it enforced by the compiler, no? – Edd Barrett May 23 '18 at 15:26
  • I honestly don't see how. If you try to send things that can't be sent, the compiler will already tell you. Substitute `Hash` or `PartialEq` for `Send` — what does enforcing that some type *not* implement a trait actually buy you? – Shepmaster May 23 '18 at 15:30
  • The problem is, in one of the implementers the stuff that I don't want to be sent is behind an opaque pointer to some data structures that Rust has no knowledge of. Otherwise it would be automatic, like you said. – Edd Barrett May 23 '18 at 15:37
  • 3
    I guess this implementer is not `Send` by virtue of that raw pointer then... – Edd Barrett May 23 '18 at 15:38
  • 1
    Thinking of trait constraints as a subtraction of capabilities is bound to bring ugly looking edge cases. It might be best fo focus on what implementers can do, rather than what they cannot. – E_net4 May 23 '18 at 16:52

2 Answers2

2

No, you can't make !Send a condition of ThreadThing. The compiler just doesn't support that kind of logic.

If it would be possible for someone using your crate to make a type that is implicitly Send, contains no unsafe code in its implementation anywhere, and make it unsafe just by implementing ThreadThing for it -- in that case, you would make ThreadThing an unsafe trait to indicate that there is unsafe code somewhere that relies on an invariant that can't be described in the type system: the invariant "Things that are Send don't implement ThreadThing".

If, as is more likely, it's only unsafe to implement Send manually for a type that implements ThreadThing -- in that case, you don't need to do anything, because manually implementing Send is unsafe already. If an implementor of ThreadThing decides to manually implement Send, they take on the burden of guaranteeing not only their own invariants, but also ThreadThing's.

trent
  • 25,033
  • 7
  • 51
  • 90
0

The answer is: yes, you can, under some very specific conditions. Whether you should need to do this is another matter.

You can define a negative trait implementation for another trait if the trait you are negating is:

  • an auto-trait.
  • from the current crate.

So the following will work (playground):

#![feature(optin_builtin_traits)]

auto trait Scary {}

trait ThreadThing { }
impl !Scary for ThreadThing {}

But it would not work if you were trying to do:

impl !Send for ThreadThing {}

Or if Scary was not an auto-trait.

Note however that, in general it should not be necessary to mark a trait !Send in this way. The concrete implementations of the trait will be marked Send or !Send by the Rust compiler based upon the contents of the implementing struct.

Edd Barrett
  • 3,425
  • 2
  • 29
  • 48
  • Sorry, this doesn't do what you think. ([playground](https://play.rust-lang.org/?gist=5143ae8d2ec3fea8396ecac9f7f71c9d&version=nightly&mode=debug)) You unimplement `Scary` for the *type* `ThreadThing` (the type of trait objects of `ThreadThing`, as in `Box` and `&ThreadThing`). [In the future](https://github.com/Ixrec/rfcs/blob/dyn-trait-syntax/text/2113-dyn-trait-syntax.md), this will be written `impl !Scary for dyn ThreadThing` and there will be a lint to warn when you use "bare trait" syntax, because it probably doesn't do what you want. – trent May 24 '18 at 11:46