Reputation: 744
I am learning Rust and came across something similar to this
struct A<T> {
some_vec: Vec<T>,
pub secret: &'static str
}
struct B{}
struct C{}
impl B {
fn foo<T>(&self) {
let bar: A<T> = A {
some_vec: Vec::new(),
secret: "123"
};
println!("The secret is {}", bar.secret);
}
}
impl C {
fn foo<T>(&self) {
let b = B{};
b.foo();
}
}
fn main() {
let c = C{};
c.foo();
}
This produces the compiler error
error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [--explain E0282]
--> src/main.rs:26:11
|>
26 |> b.foo();
|> ^^^
I have seen posts about the same error message but I was unable to figure out the solution in this case. What information (related to b.foo()
) am I missing to specify?
Upvotes: 0
Views: 363
Reputation: 300299
Since type inference is a novelty to many coming to Rust, let's explain it a bit more in depth.
In essence, type inference is a bit like those "maze" games you may have played in magazines:
You have a bunch of places where the type is unknown on the left hand, to connect to a handful of known types on the right hand. How to? Well, by examining the relationships between types!
The simplest relationship (beyond no need to guess) is a 1-degree relationship:
let x: String = "Hello, World".into();
In Rust, we know that into()
comes from the Into<T>
trait that is implemented by &str
, but which T
? Well, into()
returns a T
and the type of the expression is expected to be a String
, therefore it must be Into<String>
.
In order to solve type inference, the compiler will therefore build some kind of graph of relationships between the places where the type need be inferred and the known types (in Rust, the function signature must be explicit, so it should not have to search too far), and then will try to deduce the types little by little.
Let's examine the current use case:
fn main() {
let c = C{};
c.foo();
}
Here, c.foo();
calls C::foo<T>()
: we need to deduce T
. What information do we have? None. Zilch. Nada.
In this case, unable to solve the problem, the compiler bails out and asks you (the developer) what you wanted T
to be. You can specify it in multiple ways:
::<>
as in c.foo::<i32>()
let x: i32 = ...;
_
character to elide part of it as in let x: Vec<_> = ...;
or c.foo::<Vec<_>>()
if the compiler has enough information to deduce _
on its own of course.Upvotes: 8
Reputation: 65907
Often, generic functions will use their type parameters in the type of their parameters. In that case, the compiler can infer the type parameters from the type of the arguments on a call.
However, in your case, B::foo
and C::foo
don't have any parameters using T
. Therefore, each call needs to specify a concrete type for T
explicitly. This is done by adding ::<X>
(where X
is a type) after the name of the function, before the parentheses.
In C::foo
, presumably, you'll want to forward C::foo
's T
to B::foo
, so you'd write:
impl C {
fn foo<T>(&self) {
let b = B {};
b.foo::<T>();
}
}
Then in main
, you can use any type you want:
fn main() {
let c = C {};
c.foo::<i32>();
}
Upvotes: 3