# Named impl with Implementation Selection Variant

**URL:** <https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374>\
**Category:** language design\
**Created:** [June 5, 2026, 5:17pm UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374 "2026-06-05T17:17:41Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![viruscamp](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/viruscamp/32/14354_2.png) [@viruscamp](https://internals.rust-lang.org/u/viruscamp)\
**Post date:** [June 5, 2026, 5:17pm UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/1 "2026-06-05T17:17:42Z")

</div>

# Rust named impl Draft

The purpose is clear: to bypass Rust's orphan rule.  
The proposed syntax should be more fluent to write, more natural to read, and more complete than the referenced proposals.

## Advantages Over Other Drafts

1. By forbidding named implementations for specific traits, [the hashtable problem](https://internals.rust-lang.org/t/revisit-orphan-rules/7795) is cleanly bypassed.
2. Allow overlapping of named trait implementations, resolving the implementation during monomorphization, thus enabling specialization and partial specialization.
3. The approach is more ergonomic, more explicit, and more complete.

## New Grammar

1. Introduce named trait implementations (named-impl)

```rust
pub impl<T> Trait1 for Struct1<T> use ST1 {
    fn trait1_fn(&self) {
        println!("named impl ST1");
    }
}

```

1. Introduce a new type definition syntax, the Implementation Selection Variant (Impl Variant for short)

```rust
type Struct1Default = Struct1<i32> + _ use default;
type Struct1WithST1 = Struct1<i32> + Trait1 use ST1;

```

## Overview

```rust
// Default impl for struct and trait, same as Rust 2024 edition
pub struct Struct1<T>(T);
pub trait Trait1 {
    fn trait1_fn(&self);
}
impl<T> Trait1 for Struct1<T> {
    fn trait1_fn(&self) {
        println!("default impl");
    }
}

// New named-impl definition, can be defined in any crate
// ST1 below lives in the Type Namespace
pub impl<T> Trait1 for Struct1<T> use ST1 {
    fn trait1_fn(&self) {
        println!("named impl ST1");
    }
}

// Forbidden: using `default` as a named-impl name
pub impl Clone for i32 use default {} // ❌

// Import with `use` just like structs and traits; the full path mod1::ST1 can also be used
use mod1::{Struct1, Trait1, ST1};

// Type definitions
type Struct1Generic = Struct1<i32>;
// A generic type, which by default resolves to the following line: Struct1<i32> + _ use default

type Struct1Default = Struct1<i32> + _ use default;
// A concrete type, behaving exactly like Struct1 in Rust 2024; no named-impl is used.
// `default` is a weak keyword here.

type Struct1WithST1 = Struct1<i32> + Trait1 use ST1;
// A concrete type, same layout as Struct1, but uses ST1's implementation for Trait1.
// The trailing `_ use default` is implicitly omitted.

// Different impl => different types
assert!(TypeId::of::<Struct1Default>() != TypeId::of::<Struct1WithST1>());

// Complex definition 1
type ComplexType1 = &(i32 + Trait1 use ST1) + Trait2 use ST2;

// Complex definition 2: overlapping implementations and priority rules
type ComplexType2 = Struct1<i64>
    + From<i32> use mod2::Struct1Fromi32
    + From<_: Add> use default
    + From<_> use StructFrom
    + Trait1 use ST1;
// Multiple named-impl generic parameters; overlapping of traits is allowed.
// When acting as From<i32>, Struct1Fromi32 is used; as From<&str>, StructFrom is used.
// The first matching trait (before `use`) from left to right wins and its implementation is used.

// Type conversions
let s = Struct1(1); // defaults to Struct1<i32> + _ use default
let mut s_ref_named: &(Struct1<i32> + Trait1 use ST1) = &s; // ✅ type conversion
let s_ref_default = s_ref_named as &(Struct1<i32> + _ use default); // ✅ mutual conversion
s_ref_named = s_ref_default; // ❌ implicit conversion without explicit type annotation is forbidden

let s4 = Struct1(4); // defaults to Struct1<i32> + _ use default
let s5: Struct1<i32> + Trait1 use ST1 = s4; // move plus type conversion

let vs: Vec<Struct1<i32>> = vec![]; // defaults to Vec<Struct1<i32> + _ use default>
let vs1: &Vec<Struct1<i32> + Trait1 use ST1> = &vs; // Struct1 with impl variant can appear anywhere in the type
let vs2: &Vec<Struct1<i32> + _ use default> = vs1;

// End usage
// Any T: Trait1 can be instantiated with both Struct1<i32> + _ use default and Struct1<i32> + Trait1 use ST1

// 1. Direct call
s5.trait1_fn();

// 2. UFCS
<Struct1<i32> as Trait1 use ST1>::trait1_fn(&s5);

// 3. Generic function
fn use_trait1<T: Trait1>(a: &T) {
    a.trait1_fn();
}

use_trait1(s_ref_default); // default impl
use_trait1(s_ref_named); // named impl ST1

// 4. Generic type
pub struct Struct2<'a, T: Trait1>(&'a T);
impl Struct2<'a, T: Trait1> {
    pub fn use_inner(&self) {
        self.0.trait1_fn();
    }
}

let s21 = Struct2(s_ref_default);
s21.use_inner(); // default impl

let s22 = Struct2(s_ref_named);
s22.use_inner(); // named impl ST1

// trait object
let mut dyn_trait1: &dyn Trait1;

dyn_trait1 = s_ref_named;
dyn_trait1.use_inner(); // named impl ST1

dyn_trait1 = s_ref_default;
dyn_trait1.use_inner(); // default impl

// impl trait
// 1. `impl trait` in argument position is merely syntactic sugar for generics
fn use_trait1(a: &impl Trait1) {
    a.trait1_fn();
}
use_trait1(s_ref_default); // default impl
use_trait1(s_ref_named); // named impl ST1

// 2. `impl trait` in return position
fn return_trait1() -> impl Trait1 {
    let s4 = Struct1(4); // defaults to Struct1<i32> + _ use default
    return s4 as (Struct1<i32> + Trait1 use ST1); // move plus type conversion
}

```

## Prohibition Rules

1. Named-impl must be forbidden for `Copy`, `Drop`, `Send`, `Sync`, `Unpin`, etc.

```rust
#[disable_named_impl(all)]
pub trait Copy {}

```

`#[disable_named_impl(all)]` can be used by external code and placed on traits, structs, enums, etc.

1. Named-impl must be forbidden for `Hash`, `Ord`, `PartialOrd`, `Eq`, `PartialEq`, etc. at least when a default implementation exists.

```rust
#[disable_named_impl(exist_default)]
pub trait Hash {}

```

`#[disable_named_impl(exist_default)]` can be used by external code and placed on traits, structs, enums, etc.

## Extended Usages

1. Implementing external traits for external types, bypassing the orphan rule.

2. Compile-time proxy, or function decorator  
(akin to `java.lang.reflect.Proxy` or Python decorators)

```rust
impl fmt::Display for i32 use ProxyDisplay {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "before ")?;
        (self as &(i32 + Display use default)).fmt(f)?;
        write!(f, " after")?;
        Ok(())
    }
}

// Generic proxy, but might be impossible to write due to recursion
impl<T: Display> fmt::Display for T use ProxyDisplayDefault {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "before ")?;
        (self as &(T + Display use default)).fmt(f)?; // ❓
        write!(f, " after")?;
        Ok(())
    }
}

```

1. Specialization, partial specialization

```rust
// Overlapping implementations and priority rules
type ComplexType2 = Struct1<i64>
    + From<i32> use mod2::Struct1Fromi32
    + From<_: Add> use default
    + From<_> use StructFrom
    + Trait1 use ST1;
// Multiple named-impl generic parameters; overlapping of traits is allowed.
// When acting as From<i32>, Struct1Fromi32 is used; as From<&str>, StructFrom is used.
// The first matching trait (before `use`) from left to right wins.

```

## Implicit Type Generics, Optional

1. Generic bounds can be structs or enums

```rust
#[derive(Default)]
struct Struct1;
impl Trait1 for Struct1 {};
impl Trait1 for Struct1 use TS1 {};
fn use_struct1<T: Struct1>() {
    T::default().trait1_fn();
}

use_struct1<Struct1 + _ use default>();
use_struct1<Struct1 + Trait1 use TS1>();

```

1. Implicit type generics: function parameters and return types become generic

- Advantage: old-style functions can automatically work with named-impl types.
- Disadvantage: unsound, breaks the function's original intent.

```rust
fn use_struct1(s: &Struct1) {
    s.trait1_fn();
}
// Automatically translates to
fn use_struct1<T: Struct1>(s: &T) {
    s.trait1_fn();
}

```

- Problem 1: Multiple `Struct1`s would be forced to the same type.
- Problem 2: If a parameter is `i32` and becomes generic, it destroys logic inside the function that relies on that `i32` (e.g., `Add` or `PartialEq`).

## Simplified Syntax

1. Named-impl selection parameter simplification

```rust
type Struct1Default = Struct1<i32> + use default;
type Struct1WithST1 = Struct1<i32> + use ST1;
type ComplexType = Struct1<i64> + use Struct1Fromi32<i32> + use StructFrom<_> + use ST1;
// The trait type can be inferred; generic parameters follow the impl name.

```

## Alternative Syntax

1. Use `as` instead of `use`

```rust
pub impl<T> Trait1 for Struct1<T> as ST1 {
    fn trait1_fn(&self) {
        println!("named impl ST1");
    }
}
type Struct1Default = Struct1<i32> + _ as default;
type Struct1WithST1 = Struct1<i32> + Trait1 as ST1;

<Struct1<i32> as Trait1 as ST1>::trait1_fn(&s5);

```

1. Named-impl selection parameters inside `<>` after generic parameters

```rust
type i32AlterDisplay = i32<i32, Display use AlterDisplay>;
type Struct1Default = Struct1<i32, _ use default>;
type Struct1WithST1 = Struct1<i32, Trait1 use ST1>;
type ComplexType = Struct1<i64, From<i32> use Struct1Fromi32, From<_> use StructFrom, Trait1 use ST1>;

//❓ How to express this with the alternative syntax?
type ComplexType1 = &(i32 + Trait1 as ST1) + Trait2 as ST2;

```

## Trait Inheritance Issues

1. Type bloat.
2. Whether a subtrait's named-impl can restrict which implementation of the supertrait is used.
3. More complex upcasting.

```rust
trait T1 {}
trait T2 : T1 {}

struct S;
impl T1 for S {}
impl T2 for S {}

impl T1 for S use T1S3 {}
impl T2 for S use T2S3 where S: T1 use T1S3 {} //❓ Optional, forces T2S3 to only be used with T1S3

// Bad news: type bloat
// Good news: the four types are not automatically generated; they don't exist unless written (compile-time and runtime)
let s1: S + _ use default = S;
let s2: S + T1 use T1S3 = S;
let s3: S + T2 use T2S3 = S; // If restricted, it is automatically inferred as the next
let s4: S + T1 use T1S3 + T2 use T2S3 = S;

type Invalid = S + T1 use default + T2 use T2S3; //❌ If the restrictive syntax above is present

```

## Compatibility Breakage

1. FFI boundaries.
2. dylib exported functions.
3. All code that assumes trait object data pointers are the same to judge identity will break.

```rust
fn is_same(a: &dyn T1, b: &dyn T1) -> bool {
	let (ptr_a, vt_a) = unsafe { transmute::<_, (usize, usize)>(a) }
	let (ptr_b, vt_b) = unsafe { transmute::<_, (usize, usize)>(b) }
	return ptr_a == ptr_b;
}

```

## Negative Implementations (negative-impls)

1. [negative-impls](https://doc.rust-lang.org/unstable-book/language-features/negative-impls.html) cannot be used for named-impl

```rust
pub impl !Trait1 for i32 use NegTrait1 {} // ❌

```

1. Overlap checking between named-impl and negative-impls is deferred to type monomorphization time

```rust
#![feature(negative_impls)]
trait DerefMut { }
impl<T: ?Sized> !DerefMut for &T { }

// Overlap with other implementations (including !Trait) is NOT checked at the definition of named-impl
impl DerefMut for &i32 use DerefMutForI32 {} // ✅

// The overlap check between named-impl and negative-impls happens at type instantiation
type X = &i32 + DerefMut use DerefMutForI32; // ❌

```

## GAT

Orthogonality with GAT

```rust
type A1 = S + Iterator use SI64; // ✅
type A2 = S + Iterator<Item=i64> use SI64; // ❌

```

```rust
struct S;

impl Iterator for S {
    type Item = i32;
    fn next(&mut self) -> Option<Self::Item> { todo!() }
}

impl Iterator for S use SI64{
    type Item = i64;
    fn next(&mut self) -> Option<Self::Item> { todo!() }
}

fn use_iter<T: Iterator>() -> T::Item { todo!() }
fn use_iter_i32<T: Iterator<Item=i32>>() -> T::Item { todo!() }
fn use_iter_i64<T: Iterator<Item=i64>>() -> T::Item { todo!() }

use_iter::<S>(); // ✅
use_iter_i32::<S>(); // ✅
use_iter_i64::<S>(); // ❌

use_iter::<S + Iterator use SI64>(); // ✅
use_iter_i32::<S + Iterator use SI64>(); // ❌
use_iter_i64::<S + Iterator use SI64>(); // ✅

```

## References

- [[Pre-RFC] Selectable Trait Implementations](https://internals.rust-lang.org/t/pre-rfc-selectable-trait-implementations/23829)
- [[Pre-RFC] Scoped `impl Trait for Type`](https://internals.rust-lang.org/t/pre-rfc-scoped-impl-trait-for-type/19923)
- [Looking for RFC coauthors on "named impls"](https://internals.rust-lang.org/t/looking-for-rfc-coauthors-on-named-impls/6275)
- [Named impls and impl generics](https://internals.rust-lang.org/t/named-impls-and-impl-generics/22158)

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [June 5, 2026, 8:46pm UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/2 "2026-06-05T20:46:52Z")

</div>

> [@viruscamp](#):
>
> References

As you linked, there's been a bunch of discussions in this area before.

What's the summary of the difference that makes this one fix the issues from the previous ones?

---

<div class="post-metadata">

**Author:** ![ProgramCrafter](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/programcrafter/32/13494_2.png) [@ProgramCrafter](https://internals.rust-lang.org/u/ProgramCrafter)\
**Post date:** [June 7, 2026, 7:13pm UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/3 "2026-06-07T19:13:49Z")

</div>

> [@viruscamp](#):
>
> 1. Named-impl must be forbidden for `Hash`, `Ord`, `PartialOrd`, `Eq`, `PartialEq`, etc. at least when a default implementation exists.
> 
> ```rust
> #[disable_named_impl(exist_default)]
> pub trait Hash {}
> 
> ```

Objectionable. I might want to sort tuples `(u32, u32)` by their second argument, or use another custom comparator, while there is a default `PartialOrd` implementation.

---

<div class="post-metadata">

**Author:** ![viruscamp](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/viruscamp/32/14354_2.png) [@viruscamp](https://internals.rust-lang.org/u/viruscamp)\
**Post date:** [June 8, 2026, 3:47am UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/4 "2026-06-08T03:47:49Z")

</div>

Let me explain the Prohibition Rules

1. Named-impl must be forbidden for Copy, Drop, Send, Sync, Unpin, etc

```rust
let a = Box::new(42);
let b = a as Box<i32> + Copy use BadCopy;
let c = b; // copy, not move
drop(b);
drop(c); // double free

```

1. Named-impl must be forbidden for Hash, Ord, PartialOrd, Eq, PartialEq, etc. at least when a default implementation exists.

```rust
struct SortedList<T: PartialOrd> {} // always keeps elements in order defined by T: PartialOrd 
let sl_org = SortedList::new((1,2),(2,3),(3,1)); // invariant: (1,2), (2,3), (3,1)
let sl_alt = SortedList::<(i32,i32) + PartialOrd use SortBy1>::new((1,2),(2,3),(3,1)); // uses different ordering, yields (3,1), (1,2), (2,3)
// Dangerous: converting an existing collection to a variant with a different comparator
let sl_bad = s1 as SortedList<(i32,i32) + PartialOrd use SortBy1>; // breaks the internal invariant

```

And so, custom comparator also breaks `BTreeMap`, custom `Hash` will break `HashMap`.

---

<div class="post-metadata">

**Author:** ![ProgramCrafter](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/programcrafter/32/13494_2.png) [@ProgramCrafter](https://internals.rust-lang.org/u/ProgramCrafter)\
**Post date:** [June 8, 2026, 10:39am UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/5 "2026-06-08T10:39:02Z")

</div>

Your last example could just forbid the cast from `SortedList<(i32, i32)>` to `SortedList<(i32, i32) + PartialOrd use SortBy1>` and not the named implementation `SortBy1` itself. After all, `PartialOrd` and `PartialOrd use SortBy1` are different traits in a single namespace, which happen to share one interface.

That makes me think that for the first half no compiler changes are needed at all: just make your traits generic. Using it ergonomically (i.e. type aliases) is another problem, though, and should depend on current codebase patterns.

```rust
pub enum StandardDef {}
pub enum RevStandardDef {}

pub trait PartialOrd<Rhs=Self, OrderingKind=StandardDef> {
    fn partial_cmp(self, rhs: Rhs) -> Option<Ordering>;
}

impl<Lhs, Rhs> PartialOrd<Lhs, RevStandardDef> for Rhs
where
    Lhs: PartialOrd<Rhs, StandardDef>
{
    fn partial_cmp(self, other: Lhs) -> Option<Ordering> {
        <Lhs as PartialOrd<Rhs, StandardDef>>::partial_cmp(other, self)
    }
}

// Or another implementation option doing approximately the same:
/*
impl<Lhs, Rhs> PartialOrd<Rhs, RevStandardDef> for Lhs
where
    Lhs: PartialOrd<Rhs, StandardDef>
{
    fn partial_cmp(self, rhs: Rhs) -> Option<Ordering> {
        <Lhs as PartialOrd<Rhs, StandardDef>>::partial_cmp(self, other)
            .map(|order| order.flip())
    }
}
*/

```

---

<div class="post-metadata">

**Author:** ![viruscamp](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/viruscamp/32/14354_2.png) [@viruscamp](https://internals.rust-lang.org/u/viruscamp)\
**Post date:** [June 8, 2026, 11:28am UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/6 "2026-06-08T11:28:30Z")

</div>

> Your last example could just forbid the cast from `SortedList<(i32, i32)>` to `SortedList<(i32, i32) + PartialOrd use SortBy1>`

No, casting is the core part. And it's almost impossible to disable special cast.

---

<div class="post-metadata">

**Author:** ![viruscamp](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/viruscamp/32/14354_2.png) [@viruscamp](https://internals.rust-lang.org/u/viruscamp)\
**Post date:** [June 8, 2026, 11:33am UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/7 "2026-06-08T11:33:43Z")

</div>

And you only need `pub fn slice::sort_by<F>(&mut self, compare: F)`.

---

<div class="post-metadata">

**Author:** ![viruscamp](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/viruscamp/32/14354_2.png) [@viruscamp](https://internals.rust-lang.org/u/viruscamp)\
**Post date:** [June 8, 2026, 11:50am UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/8 "2026-06-08T11:50:58Z")

</div>

## Advantages Over Other Drafts

1. By forbidding named implementations for specific traits, [the hashtable problem](https://internals.rust-lang.org/t/revisit-orphan-rules/7795) is cleanly bypassed.
2. Allow overlapping of named trait implementations, resolving the implementation during monomorphization, thus enabling specialization and partial specialization.
3. The approach is more ergonomic, more explicit, and more complete.

---

<div class="post-metadata">

**Author:** ![Tamschi](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tamschi/32/11541_2.png) [@Tamschi](https://internals.rust-lang.org/u/Tamschi)\
**Post date:** [June 13, 2026, 9:15am UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/9 "2026-06-13T09:15:22Z")

</div>

> [@viruscamp](#):
>
> 1. […] at least when a default implementation exists.

This breaks adding or broadening a trait `impl` being SemVer-safe.

> [@viruscamp](#):
>
> 1. By forbidding named implementations for specific traits, [the hashtable problem](https://internals.rust-lang.org/t/revisit-orphan-rules/7795) is cleanly bypassed.

The "hashtable problem" is a stand-in for the general category of traits with consistency requirements. This includes third-party definitions like the (de)serialisation traits in Serde or the reflection traits in [facet](https://lib.rs/crates/facet) (in combination with type keying).

It's not quite a soundness problem, but the logical consistency aspect here is important enough that, in my opinion, no proposal that special-cases specific traits can solve it ergonomically.

---

<div class="post-metadata">

**Author:** ![viruscamp](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/viruscamp/32/14354_2.png) [@viruscamp](https://internals.rust-lang.org/u/viruscamp)\
**Post date:** [June 13, 2026, 9:49am UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/10 "2026-06-13T09:49:58Z")

</div>

> [@Tamschi](#):
>
> This breaks adding or broadening a trait `impl` being SemVer-safe.

Actually, It's not SemVer-safe right now.

- lib A-1.0: `struct A`
- bin B:

```rust
trait T {
	fn fn1();
}
impl T for A {
	fn fn1() {}
}
A::fn1();

```

- lib A-1.1, add a new trait and impl, it breaks bin B

```rust
struct A;
trait T1 {
	fn fn1();
}
impl T1 for A {
	fn fn1() {}
}

```

---

<div class="post-metadata">

**Author:** ![Tamschi](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tamschi/32/11541_2.png) [@Tamschi](https://internals.rust-lang.org/u/Tamschi)\
**Post date:** [June 13, 2026, 9:52am UTC](https://internals.rust-lang.org/t/named-impl-with-implementation-selection-variant/24374/11 "2026-06-13T09:52:54Z")

</div>

> [@viruscamp](#):
>
> - […] it breaks bin B

No, it doesn't because `A::fn1();` in B won't try to resolve to A's implementation unless B explicitly imports `T1`.

* * *

Edit: The case that would break it here (or potentially silently change behaviour!) is if A added an associated function `fn1` on `A` directly.  
This is usually considered acceptable in that the unqualified call in B is considered fragile.

It's better practice to qualify it as `::<A as T>::fn1()` there for such static calls. That's the reason why I think of "extension traits" that are intended to be called into statically without qualification as a bit sketchy sometimes.
