Idea: Substitution of self-explanatory type parameters via '_'
In many cases, function parameters take self-explanatory structs or enumerations as arguments. In this case, even though the name of the structure is self-explanatory, such as <func_name>Param
, if the function is hidden inside a deep dependency, for example, foo::bar::baz::my_fn
, the function call will look pretty messy, like foo::bar::baz::my_fn(foo::bar::baz::MyFnParam::new())
.
I propose to extend the use of the _
keyword and use it to substitute the type name of a self-explanatory parameter, as shown above. Here's a quick example
pub struct FooParam {
param1: i32,
param2: &'static str,
}
impl FooParam {
pub fn new()
}
fn foo(param: FooParam) {
// ...
}
// NOW
foo(FooParam::new())
foo(FooParam {
param1: 1,
param2: "hello",
})
// TO BE
foo(_::new())
foo(_ {
param1: 1,
param2: "hello",
})
This can be implemented simply by replacing _
with its type when the expression provided as a function argument is path and the first segment is _
.
These substitutions can be applied equally well to enumerations, and again help to reduce the verbosity of writing self-explanatory forms like the one above.
let mut state = some_very_long_module_name::AppState::FooFoo;
// NOW
state.go_to(some_very_long_module_name::AppState::BarBar);
// TO BE
state.go_to(_::BarBar);
// NOW
some_very_long_module_name::AppState::zoo(
some_very_long_module_name::AppState::FooFoo + some_very_long_module_name::AppState::BarBar
);
// TO BE
// 'First path segment underscore' of every expression is replaced into the parameter's type
some_very_long_module_name::zoo(_::FooFoo + _::BarBar);
// MODULE DEFINITION ...
mod some_very_long_module_name {
pub enum AppState {
FooFoo,
BarBar,
BazBaz,
}
impl AppState {
fn go_to(&mut self, next: Self) {
// ...
*self = next;
}
fn zoo(a: Self) -> Self {
panic!("...")
}
}
impl std::ops::Add for AppState {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
_ => panic!("..."),
}
}
}
}
This can be applied to traits in similar fashion.
mod inner {
pub trait MyTrait: Sized {
fn pewpew() -> Self {
self
}
}
pub struct SomeImplementor;
impl MyTrait for SomeImplementor {}
fn foo(_: SomeImplementor) {}
fn nope(_: impl MyTrait) {}
}
// NOW
inner::foo(<inner::SomeImplementor as inner::MyTrait>::pewpew());
// TO BE
inner::foo(<_ as inner::MyTrait>::pewpew());
// NOW
inner::nope(inner::SomeImplementor);
// TO BE: NOPE!
// inner::nope(_); // _ for impl MyTrait: Can't be inferenced!
Or, in builder patterns ...
#[derive(Builder)] // Suppose we've added awesome builder crate dependency
struct AVeryVeryLongStructNameWhichDerivesBuilderForItsConstruction {
foo: i32,
bar: i32,
baz: i32,
}
fn acceptor(s: AVeryVeryLongStructNameWhichDerivesBuilderForItsConstruction) {}
// NOW
acceptor(
AVeryVeryLongStructNameWhichDerivesBuilderForItsConstruction::builder()
.foo(1)
.bar(2)
.baz(3)
.construct()
);
// TO BE
acceptor(_::builder().foo(1).bar(2).baz(3).construct());
It'd be nice to have this.