We are excited to announce that RFC 3323 "Restrictions" is ready for testing on nightly Rust. In this post, we will briefly describe the features. If you are already familiar with them, you can skip ahead to the How can I help? section.
The RFC is split into two features: impl_restriction and mut_restriction.
What is impl_restriction?
The impl_restriction feature allows explicit restriction of the scope in which a trait may be implemented.
For example, consider a trait Foo with a method that we want users to be able to call, while preventing downstream crates from providing their own implementations. With this feature, we can write:
#![feature(impl_restriction)]
pub impl(crate) trait Foo {
fn method();
}
impl Foo for usize {
fn method() {}
}
The impl(crate) restriction prevents Foo from being implemented outside the current crate. As with pub, other paths can also be specified, such as impl(super) or impl(in path).
Without this feature, this use case is typically handled using the sealed trait pattern, which is described in the Rust API Guidelines.
In short, this pattern defines a public Sealed trait inside a private module and makes it a supertrait of Foo. Because downstream crates cannot name Sealed, they cannot implement Foo.
pub trait Foo: private::Sealed {
fn method();
}
// Implement `Foo` for selected types.
impl Foo for usize {
fn method() {}
}
mod private {
pub trait Sealed {}
// Implement `Sealed` for those same types, but no others.
impl Sealed for usize {}
}
However, this pattern requires defining an additional Sealed trait. The new impl_restriction feature provides a more direct and concise way to express the same restriction.
The feature also allows the compiler to produce a more direct error message when an implementation is attempted outside the permitted scope.
For example, the following code:
#![feature(impl_restriction)]
pub mod foo {
pub mod bar {
pub(crate) impl(super) trait Foo {}
}
// `Foo` may be implemented here.
impl bar::Foo for i8 {}
}
// Error: `Foo` cannot be implemented here.
impl foo::bar::Foo for u8 {}
results in the following error:
error: trait cannot be implemented outside `crate::foo`
--> src/lib.rs:12:1
|
12 | impl foo::bar::Foo for u8 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: trait restricted here
--> src/lib.rs:5:20
|
5 | pub(crate) impl(super) trait Foo {}
| ^^^^^^^^^^^
What is mut_restriction?
The mut_restriction feature allows explicit restriction of the scope in which a field may be mutated.
For example, consider a struct Bar with a field alpha that can be read from any crate but can only be mutated within the crate where Bar is defined. With this feature, we can write:
#![feature(mut_restriction)]
pub struct Bar {
pub mut(crate) alpha: u8,
}
The mut(crate) restriction prevents alpha from being mutated outside the current crate. As with pub, other paths can also be specified, such as mut(super) or mut(in path).
This provides an alternative to getter methods by allowing read-only access through direct field access. It can also work better with the borrow checker, which can track borrows at the field level and allow disjoint fields to be borrowed independently.
When code attempts to mutate a restricted field outside the permitted scope, the compiler produces a direct error message. For example, the following code:
#![feature(mut_restriction)]
pub mod foo {
pub struct Bar {
pub mut(self) alpha: &'static str,
}
impl Bar {
pub fn mutate_inner(&mut self) {
// `alpha` may be mutated here.
self.alpha = "inner";
}
}
}
impl foo::Bar {
pub fn mutate_outer(&mut self) {
// Error: `alpha` cannot be mutated here.
self.alpha = "outer";
}
}
results in the following error:
error: field `alpha` cannot be mutated outside `crate::foo`
--> src/lib.rs:19:9
|
5 | pub mut(self) alpha: &'static str,
| --------- field restricted here
...
19 | self.alpha = "outer";
| ^^^^^^^^^^^^^^^^^^^^
The mut_restriction feature can also be used with fields of enum variants and unions. For example, we can write:
pub enum Foo {
Alpha { mut(crate) x: u8 },
Beta(mut(crate) u8), // We can also use tuple-style
}
pub union Bar {
pub mut(crate) i: i32,
pub f: f32,
}
pub struct Baz(pub mut(crate) u8); // We can also use tuple-styleRestricting construction with struct expressions
A common reason to use mut-restricted fields is to preserve an invariant. However, allowing unrestricted construction with struct expressions would let users create values that have not been validated, potentially violating that invariant.
Thus, we disallow struct expressions if any mut-restricted field cannot be mutated from the current scope. For example, the following code:
#![feature(mut_restriction)]
pub mod foo {
pub struct Baz {
pub alpha: u8,
pub mut(self) beta: u8,
}
}
fn main() {
let bar = foo::Baz { alpha: 0, beta: 1 };
}
results in the following error:
error: `Baz` cannot be constructed using a `struct` expression outside `crate::foo`
--> src/main.rs:11:15
|
6 | pub mut(self) beta: u8,
| --------- field restricted here
...
11 | let bar = foo::Baz { alpha: 0, beta: 1 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^How can I help?
We'd love for you to try out these new features and share your feedback! To get started, use the latest Rust nightly compiler1 and enable #![feature(impl_restriction)] or #![feature(mut_restriction)].
Please share your feedback in the dedicated feedback issue.
In particular, the RFC lists several unresolved questions. One of the main open questions concerns the syntax, which is still open to discussion. Please try out the features and let us know what you think!
Acknowledgements
These features were implemented as part of a Google Summer of Code 2026 project. I would like to thank my mentors, Jacob Pratt and Urgau, for their guidance and support throughout the project.
-
Make sure to run
rustup update nightlyfirst, as these features are very new and may not be available on an older nightly toolchain. ↩