Fully Qualified Syntax
Fully qualified syntax names exactly which trait implementation or associated item you mean, using forms such as <Type as Trait>::item.
What it is
Rust usually resolves method calls from the receiver type and in-scope traits.
When names collide, you can be explicit.
For trait methods that take self, Trait::method(&value) is often enough.
For associated functions, associated constants, and associated types without a receiver, use the fully qualified form.
The general shape is:
<Type as Trait>::function(receiver_if_method, other_args...).
For associated constants and associated types, use <Type as Trait>::CONST or <Type as Trait>::Assoc.
This syntax is not only for conflict resolution.
It also documents intent in generic code and in code that calls trait methods with common names such as to_string, from, default, or fmt.
How it works
If a type has an inherent item and a trait item with the same name, inherent methods win for ordinary method-call syntax. Trait methods can still be called through the trait name or fully qualified syntax.
Associated functions without self need more help because there is no receiver value to identify the implementing type.
Animal::baby_name() is ambiguous if many types implement Animal.
<Dog as Animal>::baby_name() tells the compiler exactly which implementation to use.
Qualified paths work in expression position and type position.
In expression position, they call or name an associated function, method, or constant.
In type position, they name associated types such as <Vec<u8> as IntoIterator>::Item.
The as Trait part can sometimes be omitted, but including it is often clearer when several traits expose the same associated item name.
Method-call syntax performs autoderef and autoref before selecting a method.
Fully qualified calls make the selected trait explicit, but you still pass the receiver as an argument for methods that take self, &self, or &mut self.
Example
trait Label {
fn name() -> &'static str;
fn describe(&self) -> &'static str;
}
struct Widget;
impl Widget {
fn name() -> &'static str {
"inherent widget"
}
}
impl Label for Widget {
fn name() -> &'static str {
"trait widget"
}
fn describe(&self) -> &'static str {
"labeled"
}
}
fn main() {
let widget = Widget;
assert_eq!(Widget::name(), "inherent widget");
assert_eq!(<Widget as Label>::name(), "trait widget");
assert_eq!(Label::describe(&widget), "labeled");
}More realistic example
use std::fmt;
struct HexByte(u8);
impl fmt::Display for HexByte {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:02x}", self.0)
}
}
impl fmt::Debug for HexByte {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HexByte({})", self.0)
}
}
fn render_with<T>(value: &T, formatter: fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result) -> String
where
T: fmt::Display,
{
struct Adapter<'a, T> {
value: &'a T,
formatter: fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
}
impl<T> fmt::Display for Adapter<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.formatter)(self.value, f)
}
}
format!("{}", Adapter { value, formatter })
}
fn main() {
let byte = HexByte(15);
assert_eq!(format!("{byte}"), "0f");
assert_eq!(format!("{byte:?}"), "HexByte(15)");
assert_eq!(render_with(&byte, <HexByte as fmt::Display>::fmt), "0f");
}This is intentionally explicit: the function pointer names the Display::fmt implementation rather than the Debug::fmt implementation.
Common errors
trait Animal {
fn baby_name() -> &'static str;
}
struct Dog;
impl Animal for Dog {
fn baby_name() -> &'static str {
"puppy"
}
}
fn main() {
// let name = Animal::baby_name();
// error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type
let name = <Dog as Animal>::baby_name();
assert_eq!(name, "puppy");
}The fix is to name both the implementing type and the trait.
trait Id {
const VALUE: u32;
}
struct User;
impl User {
const VALUE: u32 = 10;
}
impl Id for User {
const VALUE: u32 = 20;
}
fn main() {
assert_eq!(User::VALUE, 10);
assert_eq!(<User as Id>::VALUE, 20);
}When an inherent item and a trait item share a name, ordinary Type::ITEM chooses the inherent one.
Use fully qualified syntax when the trait item is the one you mean.
Best practice
- ✅ Use fully qualified syntax at name collisions rather than relying on readers to infer the intended item.
- ✅ Prefer
<Type as Trait>::associated_itemfor receiver-less associated items. - ✅ Use trait-qualified methods such as
ToString::to_stringin iterator adapters when a named function is clearer than a closure. - ✅ Keep imports narrow if they make method resolution harder to understand.
- ✅ Use qualified associated types in complex bounds to show exactly which trait’s
Item,Output, orErroris meant. - ✅ Reach for this syntax in examples and tests when the distinction is the behavior under test.
Pitfalls
- ⚠️
Trait::associated_function()may be ambiguous without an implementing type; use<Type as Trait>::associated_function(). - ⚠️ Do not assume method-call syntax chooses a trait method when an inherent method with the same name exists.
- ⚠️ Fully qualified syntax can become noisy; use it where it clarifies real ambiguity.
- ⚠️ Do not import multiple extension traits with same-named methods into a module without making call sites clear.
- ⚠️ Associated constants and associated types need the same disambiguation as functions; this is not only a method-call issue.
See also
Function Pointers · Associated Constants · Operator Overloading · Traits · Trait Objects · Type Aliases · Iterator · Associated Types · Advanced Types & Features
Sources
- The Rust Programming Language, ch. 20.2 “Disambiguating Between Identically Named Methods” — the-book, https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#disambiguating-between-identically-named-methods
- The Rust Reference, “Qualified paths” — the-reference, https://doc.rust-lang.org/reference/paths.html#qualified-paths
