I've run into this issue using rocket that I'm trying to make sense of. I have an endpoint that takes a JSON body that's deserialized into a struct that is then borrowed by a function. Depending on how it's written, it seems to need two ampersands even though it's just a simple reference and I'd like to know why:
[#derive(Deserialize)]
struct UserInput {
// ...
}
fn uses_input(input: &UserInput) {
// ...
}
fn endpoint(input: Result<Json<UserInput>, JsonError>) -> Result<whatever> {
// Fails with `expected struct `UserInput`, found struct `rocket_contrib::json::Json``
uses_input(&input?);
// fails with `expected struct `UserInput`, found struct `rocket_contrib::json::Json`
uses_input(&(input?));
// Works
uses_input(&&input?);
// Works
let input = input?;
uses_input(&input);
// ...
}
I’m not entirely sure how you are getting the &UserInput from the &AnalyticsFilter...
sure, Json<T> implements Deref<Target=T>, so &input? which is originally of type &Json<AnalyticsFilter> can be (implicitly) coerced into &AnalyticsFilter. Maybe you can tell us more about where this type “AnalyticsFilter” comes from and how it converts into UserInput
I guess the answer is: Type inference gets unhappy with your code (for some reason) and rejects to insert a coercion in one case, but is happy to do so in the other case. Note that double-references like returned by writing && can implicitly coerce back down to &.
This might be a bug and might be fixed in the future... In the meantime a nicer alternative to &&input? might be to write &*input?.
So it seems like Rust’s type inferrence thought to itself “oh, nice, a reference to a match expression, passed to a function expecting &B; well that must mean that the match expression has type B, case closed”. Then a little later it realizes that if the match expression has type B then every match arm must return a value of type B, in particular the x in the first arm must be of type B. There’s a type mismatch now, and right hand sides of match arms are unfortunately no coercion sites.
(which compiles, try modifying the playground above)
in which case the whole situation is “a reference-to-a-reference is passed to a function expecting a reference-to-B; ah type mismatch but wait – function arguments are coercion sites, so we’ll introduce a coercion. Now what is coerced to what? Type-checking the match statement reveals that it’s of type A, so we need to coerce &&A into &B; no problem!
In case that isn't clear, I'm just explaining just how I make sense of the error messages the compiler gives me by guessing what might be going on here. I have no idea how the type checker actually operates.
I wrote up the examples that follow, and afterwards found Issue 23014 and Issue 57749. Sure enough, if you make B unsized (e.g. type A = String; type B = str;), the failing examples succeed (n.b. I didn't try every one).
I wondered how much ?'s match and Into-ness had to do with it and tried a few more things:
// A:Works
bar(&a.unwrap());
// B: Error: mismatched types
bar(&{ a.unwrap() });
// C: Works again
bar(&&{ a.unwrap() });
There are no match arms (or Into) here, so I think it's an introduction of a block that does it.
These next examples are based on Option, but you can try them with Result by using a.as_ref().map_err(|&e| e) to remove the & from the Err variant.
// E: Works
bar(a.as_ref()?);
// F: Error: mismatched types
bar(&a.as_ref()?);
// G: Works again
bar(&&a.as_ref()?);
Or these, which work with Result and make the block explicit.
// I: Works
bar(a.as_ref().unwrap());
// J: Works
bar(&a.as_ref().unwrap());
// K: Error: mismatched types
bar(&{ a.as_ref().unwrap() });
// L: Works again
bar(&&{ a.as_ref().unwrap() });