This started as discussion in the URLO, but I after poking around I have realized that I couldn't find the documentation which explains how type inference rules work with enough details to reason for cases like these. Even in the documentation for the rustc developers.
TL;DR: Somehow if you give compiler an option to read usize
— it takes it, but if you say “hey, you can read both usize
and T
” then there are no ambiguity: compiler just decides you would now read T
exclusively. And I couldn't even understand if that's bug or feature (is that actually described somewhere?).
Note: somehow everything works with vector! There are no ambiguity and both vectors or usizes
and vector of T
can be read!
Is there any way which may help one to understand what is suppose to work and what would fail?
Somehow recommendation “just add or remove restrictions randomly till compiler would be happy” doesn't sound reasonable.
The code in question looks like this:
impl<T> Matrix<T> {
fn read(path: &str) -> io::Result<Self>
where BufReader<File>:
ReadNumeric<usize> + ReadVector<usize> + ReadVector<T>
{
…
let nnz = f.read_numeric()?;
…
}
}
And as you can see nnz
(of type usize
) can be happily read. But if you do the following:
impl<T> Matrix<T> {
fn read(path: &str) -> io::Result<Self>
where BufReader<File>: ReadNumeric<usize> + ReadNumeric<T> +
ReadVector<usize> + ReadVector<T>
{
…
let nnz = f.read_numeric()?;
…
}
}
Then, suddenly, nnz
is inferred as T
:
|
52 | impl<T> Matrix<T> {
| - this type parameter
...
61 | data: f.read_vector(nnz)?,
| ^^^ expected `usize`, found type parameter `T`
|
= note: expected type `usize`
found type parameter `T`
And even if you try to specify which type you want T is still preferred:
impl<T> Matrix<T> {
fn read(path: &str) -> io::Result<Self>
where BufReader<File>: ReadNumeric<usize> + ReadNumeric<T> +
ReadVector<usize> + ReadVector<T>
{
…
let nnz: io::Result<usize> = f.read_numeric();
let nnz = nnz?;
…
}
}
leads to
52 | impl<T> Matrix<T> {
| - this type parameter
...
61 | let nnz: io::Result<usize> = f.read_numeric();
| ----------------- ^^^^^^^^^^^^^^^^ expected `usize`, found type parameter `T`
| |
| expected due to this
|
= note: expected enum `Result<usize, _>`
found enum `Result<T, _>`
P.S. Note that code which was supposed to work looked like this:
impl<T> Matrix<T> {
fn read(path: &str) -> io::Result<Self>
where BufReader<File>: ReadNumeric<usize> + ReadNumeric<T>
{
…
let nnz = f.read_numeric()?;
…
}
}
Type inference doesn't work here at all, but if you resolve all ambiguities explicitly — it works.