2.9. impl
An impl block is a container that attaches methods to a type or implements a trait for a type.
2.9.1. Two forms
The grammar of an impl block has two forms:
impl_block ::= "impl" generics? type_expr "{" impl_item* "}"
| "unsafe"? "impl" generics? type_expr "for" type_expr where_clause? "{" impl_item* "}"
| "impl" generics? "!" type_expr "for" type_expr where_clause? "{" "}"
impl_item ::= fn_decl
| "type" identifier "=" type_expr ";"
| const_decl
In impl <, the < opens generics only when a generic parameter follows (<>, <T>, <T:, <T,, <T =, <const N); otherwise it begins the target type, e.g. an inherent impl <A as P>::Out { .. }.
Inherent impl: attaching methods
First, an inherent impl:
impl Point {
fn origin() -> Point { Point { x: 0.0f64, y: 0.0f64 } }
fn distance(self, other: Point) -> f64 { ... }
}
This form is impl Type { ... }, and the body is a collection of function definitions. The functions defined this way become methods of the type.
Fixed arrays and tuples can also have inherent impls. The standard library uses this to give arrays len() / is_empty().
impl<T, const N: usize> [T; N] {
fn len(self) -> usize { N }
}
impl<A, B> (A, B) {
fn swap(self) -> (B, A) { (self.1, self.0) }
}
Trait impl: implementing a trait
Next, a trait impl:
impl Describe for Point {
fn describe(self) -> str { f"({self.x}, {self.y})" }
}
impl Add for Point {
type Output = Point;
fn add(self, rhs: Point) -> Point { ... }
}
This form is impl Trait for Type { ... }. The body contains the bodies of the methods required by the trait and the concrete definitions of associated types. Items for which the trait provides a default method may be omitted from the body, in which case the default body is used.
An impl method must have the trait declaration's parameter count, the same ref on each parameter other than the receiver, and return a value exactly when the declaration does; otherwise it is an error at the impl, and the error shows the declaration. The throws of an impl method must be the same as the throws of the trait method. An impl cannot add throws or widen the error type (4.14.11).
Send / Sync are automatic marker traits with two manual declarations (stdlib 2.13.9).
- Opt-in:
unsafe impl Send for X {}.unsafe implmay only be used forSend/Sync, and is required for manual implementations of these two traits. - Opt-out:
impl !Send for X {},impl<T> !Send for Cell<T> {}. It prevents the automatic implementation, and the body must be empty.!may only be used withSend/Sync.
Constants in an impl
const N: T = v; inside an impl block is an associated constant of that type. It is read as Type::N from outside and Self::N inside the impl. Associated constants declared by a trait (2.8.3a) get their values in the trait impl.
impl Circle {
const UNIT: f64 = 1.0;
fn unit() -> Circle { Circle { r: Self::UNIT } }
}
let r = Circle::UNIT;
An impl target or trait argument may be a projection (<T as Tr>::X) or name an item declared later; it is resolved after all items are registered.
An impl target may name a type alias, generic or not. The alias is expanded, and the impl is the one written with the aliased type: with type Al<T> = S<T>;, impl<T> Al<T> { .. } is impl<T> S<T> { .. } and impl<T> Tr for Al<T> is impl<T> Tr for S<T>. An alias that fixes some arguments (type Names<V> = Dict<str, V>;) gives an impl that covers only those instances.
2.9.2. The self convention for methods
For a function in the body to be an instance method, its first parameter must be self (or ref self).
impl Point {
fn origin() -> Point { ... } // static - called as Type::method()
fn distance(self, other: Point) -> f64 { ... } // instance - called as instance.method()
fn replace_with(ref self, other: Point) { ... } // ref self - may reassign the caller's variable
}
The exact meaning of the self parameter and the behavior of ref self are defined in the function specification. This document only notes that the presence of self distinguishes static methods from instance methods.
2.9.3. Multiple impl blocks for one type
Inherent impls for the same type may be split into several blocks.
impl Point {
fn origin() -> Point { ... }
}
impl Point {
fn distance(self, other: Point) -> f64 { ... }
}
This is only a visual and organizational grouping; the result is the same as one combined body.
However, a given trait can be implemented only once for the same type.
impl Display for Point { ... }
impl Display for Point { ... } // ERROR - duplicate implementation
2.9.4. Generic impl
An impl can also be declared generic.
impl<T> List<T> {
fn len(self) -> usize { ... }
}
impl<T> Add for Pair<T> where T: Add<T> {
type Output = Pair<T::Output>;
fn add(self, rhs: Pair<T>) -> Pair<T::Output> { ... }
}
Type parameters are declared with <...> after impl, and may appear in the target type, the trait arguments, and associated types. Additional constraints can be added with a where clause, in the same way as for trait declarations.
A generic impl covers the instances of its target that its parameters can produce: impl<V> P<str, V> gives methods to P<str, i32> but not to P<i32, i32>.
2.9.5. Orphan rule
Implementing a trait defined in one package for a type defined in another package is forbidden.
// trait Display and type i32 are both defined in the standard library (another package)
impl Display for i32 { ... } // ERROR - both from external packages
So at least one must hold: the trait is defined in the current package, or the target type is defined in the current package. Built-in types (primitives, tuples, arrays, function types) count as defined in rok::core.
The unit of this rule is the package of Source units. Different modules within the same package may therefore impl freely.
This rule prevents two packages from simultaneously creating conflicting impls for the same (trait, type) pair, ensuring coherence.
Coherence
A (trait, type) pair has only one impl, and there is no specialization.
- A generic
implis monomorphized for each type it is used with. - A concrete
imploverlaps a genericimplonly when that concrete type satisfies the genericimpl's bounds. - Two generic
impls overlap if their targets (and trait arguments) unify; this is reported as an error at declaration. Bounds on parameters that remain generic after unification do not separate them. It is an error even if no type yet satisfies both bounds (same as Rust's coherence). - For overlap,
T?andT | noneare the same type. - Negative reasoning is done only for known types.
- A parameter that becomes a concrete type through unification has its bound checked.
impl<U: Num> X for Pair<U, i32>andimpl<U> X for Pair<str, U>unify withU = str, butstr: Numdoes not hold, so they do not overlap. - A bound on a known type constructor (
List<T>: Iterator) separates the two if none of that trait's impls targets that constructor or a bare parameter. Soimpl<I: Iterator> IntoIterator for Iandimpl<T> IntoIterator for List<T>can coexist.
- A parameter that becomes a concrete type through unification has its bound checked.
impl<T: Display> Describe for List<T> { ... }
impl<T: Hash> Describe for List<T> { ... } // ERROR - i32 satisfies both Display and Hash
impl<T: Shape> Area for T { ... }
impl<T: Color> Area for T { ... } // ERROR - T and T unify (bounds irrelevant)
impl<T: Display> Describe for T { ... }
impl Describe for i32 { ... } // ERROR - overlaps the impl above since i32: Display
2.9.6. Method visibility
Methods of an inherent impl are module-private by default. They can be called freely within the same module; exposing them externally requires an explicit pub.
impl Account {
pub fn balance(self) -> f64 { self.amount } // public
fn _internal_check(self) { ... } // module-internal only
}
This matches the visibility model of struct and enum. An impl block itself is only a visual grouping, not a unit of visibility; the unit of visibility is always the module.
Methods of a trait impl follow the trait's visibility and cannot be marked separately.
2.9.7. Method conflicts between inherent and trait impls
If the same method name exists in both an inherent impl and a trait impl, the call site must choose unambiguously. The exact resolution rules are defined in Methods.
2.9.8. No defining the same method twice
Defining two methods with the same name for the same type is a compile error, whether in one impl block or in several inherent impl blocks (2.9.3), and within one trait impl (there is no overloading). Methods of the same name from different traits are allowed (2.9.7).