RFC 3678, "Trait method impl restrictions (final methods)", was accepted in January 2026 and is implemented on nightly behind #![feature(final_associated_functions)]. What people are fighting about now is twofold: whether forbidding overrides is the right call at all, and how a final fn should behave when called through dyn. The FCP on whether the standard library may ship stable APIs that use final fn is still open.

Why this, why now

The problem

A Rust trait serves two audiences: callers and implementors. A provided method (a method with a default body) serves both with a single declaration.

trait Read {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
    // provided: callable, and implementors can override it
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { /* loop over read */ }
}

When you want something callable whose body nobody can swap out, every option so far has been awkward:

  1. Write "do not override" in the docs. Nothing enforces it. If unsafe code assumes the provided implementation is correct, one bad override is UB. The RFC's example is Read::read_buf_exact: unsafe callers must defend against an implementation that returns Ok(()) without filling the buffer.
  2. Make the whole trait an unsafe trait. Every implementor then carries a safety contract for the sake of one method.
  3. Extension trait plus blanket impl. trait ReadExt: Read { fn foo(&self) {..} } impl<T: Read + ?Sized> ReadExt for T {}. This blocks overrides, but users must import a second trait, so the API splits in two.
  4. Make it a free function in the module. Like core::slice::range, which lives outside the trait because overriding it on a trait could be unsound. You lose method syntax.
  5. Make it uncallable, or gate it with a token type (for example, require a private token argument). SkiFire13's summary is that today the only alternatives are worse: make the trait unimplementable, or make the method uncallable downstream.
  6. impl dyn Trait { .. }. You can write inherent methods on the dyn type, but they can't be called through impl Trait or generics.

The other practical motivation is Error::type_id. Being able to override it would break the soundness of downcast, so it has stayed unstable for years, blocked with private-type workarounds (rust#60784).

How it works

Syntax and static rules

#![feature(final_associated_functions)]

trait MyTrait: Display {
    final fn method(&self) {
        println!("MyTrait::method: {self}");
    }
}