4

I ran into a weird situation earlier. I wrote:

if rand::random() < self.food_chance {...}

(self.food_chance is of type f32)

and I got a type inferencing error:

   |
71 |             if rand::random() < self.food_chance {
   |                ^^^^^^^^^^^^ cannot infer type for `T`

However this code compiles

if self.food_chance > rand::random() {...}

And I'm left wondering. Is there a reason for this behaviour? Is it even intended behaviour?

I know a little bit about type inferencing theory, and I know that most of the algorithms are agnostic to lhs/rhs, so I'm tempted to think this is expected behaviour rather than straight up a bug.

I tried searching around, but I never got anything close to this.

2 Answers 2

7

That's basically because Rust can never infer the type of the receiver. The type of the receiver must be known to perform method lookup, and since

rand::random() < self.food_chance

is equivalent to

std::cmp::PartialOrd::lt(&rand::random(), &self.food_chance);

the receiver is the left-hand operand.

In the expression

self.food_chance > rand::random()

on the other hand, the receiver is known, so Rust can perform method lookup. It will only find a single implementation for f32 as the receiver, namely PartialOrd<f32>, which then in turn determines the type of the right-hand side. If there were implementations for different right-hand side types, Rust would not be able to infer the type in that case either.

To understand why Rust can't infer the receiver, take a look at how Rust's method lookup process works. The first step is to build a list of candidate types based on the type of the receiver. This obvious only works when you already know the type of the receiver.

Sign up to request clarification or add additional context in comments.

6 Comments

Would you happen to know if this is a limitation of the language (impossible to resolve otherwise) or merely a limitation of the current implementation?
@MatthieuM. The current language definition simply does not contain a process for inferring the type of the receiver. The definition of the method lookup process (see link in the answer) certainly requires that the type of the receiver is known in advance. If we wanted to infer the type of the receiver, we would need to invent different method lookup semantics for the case that the receiver is not known. I can't think of any reasonable and consistent way of defining semantics for this.
I think it should be possible by treating all arguments equally; select a list of candidate methods by simple name look-up (inherent + visible traits), then use inference on receiver & argument to find which candidate could match, reject if there's any ambiguity. What I am not sure of is whether there are cases were the algorithmic complexity would explode, making it pratically impossible to resolve.
@MatthieuM. You probably could define a language with these semantics, but it would be quite different from Rust.
I may be naive, but I don't see any semantic difference here; just a difference in how to infer the types.
|
3

The > operator is sugar for a method; the partial_cmp method of the PartialOrd trait. The method is called with the left-hand side as the self argument; this determines which implementation of PartialOrd is used. Because you could implement the PartialOrd trait for different types with f32 as the right-hand side type, the implementation of PartialOrd is only uniquely determined if the type of the left-hand side is known. This implementation of PartialOrd, in turn, determines the type required from rand::random.

2 Comments

You argue that there could be different types with f32 as right-hand side, while there can be only one implementation with f32 as left-hand side. This is not true, as you can see in this example. There is only a single implementation with f32 as left-hand side, but there is also only a single implementation with f32 as right-hand side, so I can't really see how this answer explains the assymetry.
My argument assumes that the OP doesn't manually implement PartialOrd for f32, in which case OP should have mentioned this, as it is relevant for the question. This is a reason the compiler is able to infer the type. I didn't state that there could be only one implementation of PartialOrd with f32 as the left-hand side. Your example does however nicely demonstrate your answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.