I have been thinking again on the defaults affect inference issue, and I see that the compiler can already perform this inferences, with just the help of some annotations. More concretely, the existence of i32 as default integer can be leveraged to set defaults in other types.
Take as example HashMap<K,V,S=RandomState>
, where the third parameter S
would seem that it would inferred to be RandomState
but it does not actually happens. For the sake of the example, we get way crazy and want to set also defaults K=String
and V=f32
. We can do the following.
trait Inference<T> {
// A NOP to be introduced everywhere to help inference.
fn inference_nop(&self,_:T) {}
}
impl Inference<i32> for HashMap<String,f32,RandomState> {}
impl<K,V,S> Inference<u32> for HashMap<K,V,S> {}
fn main() {
let map = HashMap::default();
map.inference_nop(0);//<--- magic here!
}
These inference_nop
s should preferably be hidden and put into everything. I suppose this would currently require a procedural macro. This works because if map
is constrained to some type other than the default then there is only the Inference<u32>
implementation and 0 is unified to 0u32. If map
is not fully constrained then it has also both Inference<u32>
and Inference<i32>
. Consecuently, the compiler disambiguates 0 into 0i32, which forces to use the Inference<i32>
impl and in turn the stated HashMap<String,f32,RandomState>
default. I made several examples in this playground.
The previous approach is all-or-nothing inference. If we prefer to have each parameter independently inferred, we may use a different trait for each default. See this playground for examples.
trait Inference1<T>{ fn inference1_nop(&self,_:T) {}}
trait Inference2<T>{ fn inference2_nop(&self,_:T) {}}
trait Inference3<T>{ fn inference3_nop(&self,_:T) {}}
impl<V,S> Inference1<i32> for HashMap<String,V,S> {}
impl<K,V,S> Inference1<u32> for HashMap<K,V,S> {}
impl<K,S> Inference2<i32> for HashMap<K,f32,S> {}
impl<K,V,S> Inference2<u32> for HashMap<K,V,S> {}
impl<K,V> Inference3<i32> for HashMap<K,V,RandomState> {}
impl<K,V,S> Inference3<u32> for HashMap<K,V,S> {}
There remain some issues when some of the parameters go against literal defaults. In the example we have V=f32
, but if there is some floating literal like 3.0
it would conflict into using the language default of f64 and the impl default of f32. Thefore, this is an error, which coincides with an old consensus.
The purpose of presenting this idea is not to implement defaults this way, since it is clearly hacky. But to show that we may consider, in most aspects, adding defaults as if we would add a trait implementation. With no need to modify the actual inference engine. With any luck it will show some light into the issue.
(I also have made myself an icon after so many years here. So I am no longer a green 'N'.)