This isn't an RFC. I just wanted to bring this up for discussion and see what other people think about this idea.
I have been programing a bit more in Scala recently, and there is a feature in scala called Implicit Parameters.
I'm not 100% knowledgeable about all the implementation details in Scala but here is a quick pseudo-rust example:
fn caller() {
let implicit name = "world";
hello(); // NOTE: no argument passed
}
fn hello(name : implicit &str) {
println!("Hello {}!", name);
}
In the example above, the hello function takes an implicit argument called name
. When the caller function calls hello, the compiler would need to wire in the implicit name
variable into the implicit name
argument.
In the web frameworks I'm using in scala, this feature is mainly used as a sort of dependency injection. Objects like database connections, rest clients, and actor systems are passed through functions implicitly.
There has been recent talk about parameterizing types and functions with Allocators and it got me thinking if a form of Implicit Parameters would be worth exploring. I'm imagining a flavor of implicit Parameters that resolve their values in 3 steps.
- Check to see if the implicit parameter was set explicitly:
Box::new(..., allocator)
- Check to see if the implicit parameter is set in the local scope:
let implicit allocator = ...;
- Implicitly add the parameter to the current functions paramaters:
-
fn my_fn()
implicitly becomesfn my_fn(allocator: implicit Allocator)
-
I imagine that standard library functions, such as Box::new()
would start requiring an implicit Allocator
argument. The user can optionally pass in a local allocator, but if they choose not to, then their function will simply require that it's caller pass it one. This will continue up untill either a local allocator is defined, or until the caller is main()
which will have the global allocator defined as allocator
. allocator
would be a special parameter set by rust, other implicit parameters would result in a compiler error if never defined.
Examples:
fn String::from_str(f: &str, allocator: implicit Allocator) -> String {
... // This function takes an allocator as a param, but that param is
} // implicitly defined by the calling code.
fn library_code() -> String { // this header is implicitly converted to
String::from_str("Hello") // library_code(allocator: implicit Allocator)
} // because no implicit param named allocator was found
fn user_code() { // this header is implicitly converted to
library_code(); // user_code(allocator: implicit Allocator)
} // because no implicit param named allocator was found
fn main() { // allocator would be a special implicit param, added by rust in main
user_code(); // So user_code will be passed the global allocator implicitly
} // The param is completely implicit from main > user_code > library_code > std
fn user_code2() { // This fn doesn't take an implicit allocator
let implicit allocator = ...; // because one was defined in it's local scope
library_code(); // So library code will use the local allocator
}
So for most situations, this wouldn't change the explicit interfaces. You would just call String::from(...)
and an allocator will be implicitly passed in down from main. But for situations that use local allocators, this would enable the control of the allocations. Even in libraries, and libraries of libraries. As library code that performs allocation would now be deferring to the caller to pass in an Allocator automatically.
Some of the benefits I can think of:
- Might be useful as a sort of dependency injection.
- People/Situations that don't care about allocators still wouldn't need to worry about them.
- People/Situations that do care about allocators would have control over library allocations, even if the library wasn't designed with custom allocators in mind.
Some major drawbacks I can think of:
- It complicates the language
- Unforeseen/unaddressed complications: such as usage with traits
- Would probably require a new edition
- Might not even be possible with a new edition since it would change the standard library, older editions would need to wire in the global allocator.
- Implicit parameters would become explicit across FFI or older editions, might be verbose.
- This feature might have negative effects on performance
- Implicit changes to users API
- Might be overly burdensome on the compiler: needing to resolve implicit arguments.
- Might encourage/enable bad design and harder to read code.