---
# System prepended metadata

title: Auto traits 2021-05-07

---

# Auto traits 2021-05-07

## Summary

Auto traits semantics in the case where an explicit impl exists are not well defined. This leads to soundness problems and affects behavior on stable.

* [#84857](https://github.com/rust-lang/rust/issues/84857)

## Semantics

1. Implicit impl is always generated for every struct S
    * Implications: Defeasability
2. Implicit impl is only generated if no explicit impl exists
    * Problem: backwards compatibility?
    * Implications: NegativeSurprise, PositiveSurprise
3. Implicit impl is only generated if no NEGATIVE impl exists
    * Implications: NegativeSurprise

## Detailed stuff

### Defeasability

Defeasable logics are logics where a positive statement can be *defeated* by a negative one. If we always add implicit impls, then we have a defeasible logic when you consider negative impls. Consider this example:

```rust=
struct Foo<T> { x: Box <T> }

// Explicit impl:
impl !Send for Foo<()> { }

// Implicit impl:
impl<T> Send for Foo<T> where Box<T> : Send { }
```

Using the implicit impl, we can prove `Foo<()>: Send`, but the negative impl seems to suggest it should not. This is where a defeasible logic would say that the negative assertion overrides the positive one. Adding defeasability makes our logic more complex: 'negative impls' are no longer something that just applies to coherence (i.e., promising that no positive impls will exist), they are something we need to take into account when trying to decide if a positive impl applies.

### NegativeSurprise

Consider our example from the previous section:

```rust=
struct Foo<T> { x: Box <T> }

// Explicit impl:
impl !Send for Foo<()> { }
```

If we omit the default impl because of the explicit negative impl, this implies that `Foo<u32>: Send` is not true. People have sometimes expressed surprise at these semantics. It is unclear though how often code like this exists in the wild or what its purpose is.

### PositiveSurprise

Consider our example from the previous section, but with a position explicit impl:

```rust=
struct Foo<T> { x: Box <T> }

// Explicit impl:
impl Send for Foo<()> { }
```

If we omit the default impl because of the explicit position impl, this implies that `Foo<u32>: Send` is not true. People have sometimes expressed surprise at these semantics. 

On the other hand, it does have the advantage that if there *are* explicit impls, one sees all the impls at play (nikomatsakis at least would potentially be surprised if the `Foo<u32>: Send` was implemented when the only visible impl is for `Foo<()>`).

It is unclear though how often code like this exists in the wild or what its purpose is. While there are some uses of this, e.g. [type maps](https://twitter.com/lcnr7/status/1295361144564207617), these are hopefully 1) fairly rare and 2) better served with actual `#[marker]` traits.

NB: If we made auto traits marker traits, for example, then one one could recover the expected semantics by allowing users to explicitly write the implicit impl as well.

### Safety concern when always considering auto impls

Always considering auto impls, even if positive impls exist, would for example cause `MutexGuard<Cell<i32>>` to implement `Sync`. cc [#41622](https://github.com/rust-lang/rust/issues/41622).

As `Sync` is an `unsafe` trait, this would be unsound.

It seems difficult to effectively detected and warn on such cases,
so lcnr would like to avoid this if at all possible.

NB: The underlying problem here is that the `MutexGuard` has an `&Mutex`; the auto trait considers this `Sync` because the *external API* is indeed `Sync`. But `MutexGuard` has access to internal methods and fields that make it not `Sync`. One might argue this is also a shortcoming of the implicit impl, and that it should inline the fields of `Mutex` into the impl.
