Currently, Rust’s . operator auto-derefs its left hand side to search for methods. However, this may lead to problems. Consider the following snippet:
Snippet 1:
use std::rc::Rc;
#[derive(Show, Clone)]
struct Answer(i32);
impl Answer {
fn answer(&self) {
println!("The answer of life, universe and everything is {}!", self.0);
}
}
fn main() {
let rc_answer = Rc::new(Answer(42));
// Calls Answer::answer.
rc_answer.answer();
// Call <Rc<Answer> as Clone>::clone, but <Answer as Clone>::clone is a thing too!
let cloned_something = rc_answer.clone();
// Call with UFCS on an Answer with implicit dispatching.
let cloned_answer = Clone::clone(&*rc_answer);
// Call with UFCS on an Rc with explicit dispatching.
let cloned_rc = <Rc<Answer> as Clone>::clone(&rc_answer);
println!("cloned_something: {:?}.\ncloned_answer: {:?}.\ncloned_rc: {:?}.",
cloned_something, cloned_answer, cloned_rc);
}
Which prints:
The answer of life, universe and everything is 42!
cloned_something: Rc(Answer(42i32)).
cloned_answer: Answer(42i32).
cloned_rc: Rc(Answer(42i32)).
Both Answer and Rc<Answer> has a clone method,and when Rust searchs for a clone to call for rc_answer.clone(), it chooses the one on Rc. However:
-
rc_answer.answer() calls a method on a value of type Answer, so it is natural for one to assume rc_answer.clone() would call clone on that Answer too. But the actual behaviour is surprising.
-
Rust prefers to be fairly explicit when it comes to solving ambiguities with method dispatching, It refuses to make a guess in the following snippet:
Snippet 2:
trait Foo { fn names_the_same(&self); }
trait Bar { fn names_the_same(&self); }
struct FooBar(i32);
impl Foo for FooBar { fn names_the_same(&self) {} }
impl Bar for FooBar { fn names_the_same(&self) {} }
fn main () {
FooBar(42).names_the_same();
}
The compiler complains:
error: multiple applicable methods in scope [E0034]
So it seems that happily accepting rc_answer.clone() in Snippet 1 is not consistent with the behaviour in Snippet 2.
The solution: Forbid . and mandate UFCS in such cases.
Pros: Consistency and less surprises.
Cons: Breaking change and a bit more typing.
EDIT: As discussed below, forbidding . here can be too heavy handed. So instead, adding a lint is a good alternative solution.
So what do you think? 