Summary
This RFC introduces the leading-dot syntax for path inference in type construction. When the type is known from context, developers can write .Variant, .Variant { field: 1 }, and .Variant(1) for enums and .{ field: 1 } and .(1) for structs instead of writing out the type name.
Motivation
When working with enums in match statements or other contexts where variants are used repeatedly, developers commonly use glob imports (use Enum::*) or single-letter aliases (use Enum as E) to reduce verbosity. Glob imports risk name collisions, and single-letter aliases are disallowed in some codebases. Leading-dot syntax provides a standardized alternative that avoids both problems while maintaining clarity about where types come from.
// Current Approach (glob import)
use *;
match my_enum
// Current Approach (single letter import)
use FooBar as F;
match my_enum
// Proposed Approach
match my_enum
Function calls with struct parameters also benefit from this syntax. Named parameters in functions have been proposed multiple times for Rust. Leading-dot syntax for structs achieves a similar goal. Combining it with default field values only makes it more comparable.
// Current Approach
my_function;
// Proposed Approach
my_function;
Guide-level explanation
When the compiler knows what type to expect, instead of writing the full type, it's possible to write the type using the leading-dot syntax.
All forms
- Enum Variant (unit):
.Variant - Enum Variant (tuple):
.Variant(1) - Enum Variant (named fields):
.Variant { value: 1 } - Struct with (named fields):
.{ value: 1 } - Struct with (tuple):
.(1)
Enum Variants
// Current Approach
update_status;
update_status;
update_status;
// Proposed Approach
update_status;
update_status;
update_status;
Match Arms
// Current Approach
match status
// Proposed Approach
match status
Struct Construction
;
// Current Approach
print_weather_data;
// Proposed Approach
print_weather_data;
Reference-level explanation
Syntax Changes
PathInExpression ::=
"::"? PathExprSegment ( "::" PathExprSegment )*
+ | "." IDENTIFIER
StructExpression ::=
- PathInExpression "{" ( StructExprFields | StructBase)? "}"
+ ( PathInExpression | "." ) "{" ( StructExprFields | StructBase)? "}"
CallExpression ::=
- Expression "(" CallParams? ")"
+ ( Expression | "." ) "(" CallParams? ")"
StructPattern ::=
- PathInExpression "{" StructPatternElements? "}"
+ ( PathInExpression | "." ) "{" StructPatternElements? "}"
TupleStructPattern ::=
- PathInExpression "(" TupleStructItems? ")"
+ ( PathInExpression | "." ) "(" TupleStructItems? ")"
Type Resolution
Path inference resolves the inferred path based on the concrete type expected at an expression or pattern position. These shall include return type annotations, function parameters, variable type annotations, or parent expressions.
Scoping and Privacy
Path inference respects Rust's normal privacy rules. If a type is private, it remains inaccessible using path inference; that is to say that the leading dot syntax does not grant any additional access to otherwise inaccessible types.
Drawbacks
Ambiguity in Functions with Multiple Enum Parameters
When multiple parameters share variant names, the leading-dot syntax can be confusing and ambiguous.
configure_wireless;
Without looking at the function signature, it's unclear which argument corresponds to which parameter. This is problematic during code review.
Note: Developers can use explicit types when they deem it to be important. They can also restructure APIs to use struct parameters where field names provide more context. The below example is much clearer.
configure_wireless;
Hidden Type Information in Code Review
When reading diffs or reviewing code without an IDE, the inferred type is not immediately visible. The below example might be difficult to understand.
configure;
Note: Ideally, the API designer should use more explicit function and field names, but this is not always possible.
Rationale and alternatives
Dot . over underscore _
In Rust, the underscore _ is already used as a placeholder for type inference in type parameters and type arguments. As such, reusing _ for path inference was considered. In the end, however, it was decided that this solution was not ideal and that dot syntax would be a better fit.
The main issue with underscores is that an underscore can be part of an identifier. As such, it's not possible to write _Variant the same way as .Variant. It would have to be written as _::Variant. This syntax is much longer and resembles a partially known type rather than an inferred type. In addition, a developer might think that it is okay to write std::_::Variant.
Additionally, the underscore _ already has many meanings, such as discarding an unused variable or acting as a placeholder. Adding on additional meanings to the underscore would reduce clarity rather than increase consistency.
Leading dot syntax clearly communicates that the entire path is being inferred from context, and it's shorter.
Prior art
Default::default()
Rust already has a form of type inference for construction: Default::default(). When the type is known from context, it's possible to write:
let settings: Settings = Defaultdefault;
People have also previously implemented path inference in a macro using Default::default().
takes_foo;
Swift
Swift, the main inspiration for this RFC, has had leading-dot syntax since its initial release in 2014. It's widely used throughout Swift codebases:
enum Status {
case pending(f64)
case complete(Data)
}
struct Data {
var foo: String
}
func make_data() -> Data {
.init(foo: "bar")
}
func make_status() -> Status {
.pending(0.5)
}
This is functionally equivalent to the following:
func make_data() -> Data {
Data(foo: "bar")
}
func make_status() -> Status {
Status.pending(0.5)
}
Unresolved questions
Generic Arguments in Enum Variants
Rust permits generic arguments after the variant for enum variants.
Pending;
Complete ;
With leading-dot syntax, the analogous forms would be:
.;
. ;
The intent of path inference is that the path should be fully inferred from context. Allowing generic arguments doesn't make much sense because the type was supposed to be inferred already. It also creates an asymmetry; structs do not have an equivalent position for variant generics. This might be super confusing.
Overall, it is not clear whether supporting generics for enum variants in path inference provides meaningful ergonomic benefits.
Future possibilities
Generic Arguments in Path Inference
A future syntax addition could allow generics to appear in inferred typebases, such as:
use ;
update_status;
update_status;
// or
update_status;
update_status;
;
foo;
bar;
Impls in Path Inference
Another feature could be allowing calls to associated functions. While an associated function does not have to return the value that is expected, it's possible to show a type mismatch error. This would allow developers to write:
let items: = .with_capacity;