# Idea: trait modules (bad name, please bikeshed)

**URL:** <https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255>\
**Category:** language design\
**Created:** [January 26, 2025, 5:51pm UTC](https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255 "2025-01-26T17:51:15Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![idanarye](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/idanarye/32/3346_2.png) [@idanarye](https://internals.rust-lang.org/u/idanarye)\
**Post date:** [January 26, 2025, 5:51pm UTC](https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255/1 "2025-01-26T17:51:15Z")

</div>

The name is bad - these are not really traits nor are they modules - but these concepts are roughly close enough to capture the idea, so let's use them for the discussion until someone can come up with something better.

# The problem

Consider, for example, [default field values in Serde](https://serde.rs/attr-default.html):

```rust
#[derive(Deserialize)]
struct Foo {
    #[serde(default = "default_bar")]
    bar: i32,

    #[serde(default = "default_baz")]
    baz: i32,

    #[serde(default = "default_qux")]
    qux: i32,
}

fn default_bar() -> i32 {
    1
}

fn default_baz() -> i32 {
    2
}

fn default_qux() -> i32 {
    3
}

```

This can be quite cumbersome - both to write and to read. The alternative is to move all these functions into the attributes - but that will just make it ugly and messy and you'll lose rust-analyzer support. Especially if they are left as strings.

# So what am I proposing?

My idea is inspired by [C#'s partial classes](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods), which allow splitting class definition between an auto-generated file and a human-maintained file.

(in Rust's case, we don't need to split to different files (since the macro generates code in the same file) and it's not really a "split" because in Rust you can already have multiple `impl` blocks for each type)

A trait module can be declared - either as a standalone or in the context of a type, and it would be nice to also have generics support:

```rust
trait mod MyType::my_trait_module<T> {
    const MY_CONSTANT: i32;

    type MyAssociatedType: Send + Sync + Debug;

    fn my_function(value: T) -> self::MyAssociatedType;
}

```

And then you'd have to implement it (in the same crate. Maybe even in the same file)

```rust
impl<T> mod MyType::my_trait_module<T> {
    const MY_CONSTANT: i32 = 42;

    type MyAssociatedType = fn() -> T;

    fn my_function(value: T) -> self::MyAssociatedType {
        // can't really think of how to implement this, I've programmed myself
        // into a corner by adding too many concepts to the example.
    }
}

```

The content of the trait module can be used as if were a module (though it does not create it's own encapsulation context) - e.g. `MyType::my_trait_module::<f32>::my_function(3.14)`.

# Why even do that?

Let's take the example from the problem statement. If this feature is implemented, Serde could add support for it that'd look something like this:

```rust
#[derive(Deserialize)]
struct Foo {
    #[serde(default = fn)]
    bar: i32,

    #[serde(default = const)]
    baz: i32,

    #[serde(default = fn)]
    qux: i32,
}

```

Which will cause the proc-macro to generate:

```rust
trait mod Foo::serde_defaults {
    fn bar() -> i32;

    const baz: i32;

    fn qux() -> i32;
}

```

And then the user, in the same module, would have to write:

```rust
impl mod Foo::serde_defaults {
    fn bar() -> i32 {
        1
    }

    const baz: i32 = 2;

    fn qux() -> i32 {
        3
    }
}

```

# So you still need to write it as functions? How is that an improvement?

This will only be an improvement if we can get help from rust-analyzer. Having just the `trait mod` without the `impl mod` would be an error, which rust-analyzer would detect and offer a code action to generate the entire `impl mod` block. If you add new methods to the `trait mod` without implementing them in the `impl mod` block it'd also be an error, and rust-analyzer should offer to fill the missing ones as well (just like it does with regular `impl Trait` blocks)

Other than that, it's also nice to not pollute the global namespace (even if the pollution is contained inside a module) and the namespacing is a bit more organized this way - but these advantages, without the rust-analyzer support, would probably not justify such a feature.

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [January 26, 2025, 6:08pm UTC](https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255/2 "2025-01-26T18:08:46Z")

</div>

This seems quite close to having an ordinary trait but without `Self` type (or with some meaningless choice of dummy type, e. g. `()`, for `Self`).

This way, there would be a compilation error _immediately_ in case you're missing the `impl`, but assuming in your described use-case the derived impl is making use of this impl anyway, it you would usually always create a compilation error in practice, after all.

---

<div class="post-metadata">

**Author:** ![idanarye](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/idanarye/32/3346_2.png) [@idanarye](https://internals.rust-lang.org/u/idanarye)\
**Post date:** [January 26, 2025, 6:11pm UTC](https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255/3 "2025-01-26T18:11:38Z")

</div>

With a regular trait, rust-analyzer does not know to offer to generate the `impl Trait` block.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [January 27, 2025, 12:44am UTC](https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255/4 "2025-01-27T00:44:17Z")

</div>

If r-a could turn “missing impl for mod” into a “generate mod impl” intention, then turning “missing impl for trait” into a “generate trait impl” intention should be just as possible.

---

<div class="post-metadata">

**Author:** ![idanarye](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/idanarye/32/3346_2.png) [@idanarye](https://internals.rust-lang.org/u/idanarye)\
**Post date:** [January 27, 2025, 1:28am UTC](https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255/5 "2025-01-27T01:28:19Z")

</div>

When rust-analyzer sees a trait definition, it doesn't know which type is _supposed_ to implement it. Or which types - since multiple types are allowed to implement the same trait. Module traits have one-one relationship between the definition and the implementation.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [January 27, 2025, 1:41am UTC](https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255/6 "2025-01-27T01:41:35Z")

</div>

If you're just asking for `Trait::function`, yes, but you can also ask for a specific type's impl, such as `<Self as Trait>::function` or `<() as Trait>::function`.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [July 21, 2026, 1:42am UTC](https://internals.rust-lang.org/t/idea-trait-modules-bad-name-please-bikeshed/22255/7 "2026-07-21T01:42:07Z")

</div>

This topic was automatically closed 540 days after the last reply. New replies are no longer allowed.
