Of course, you could create a free standing function in those cases. But then you don't use the advantages of method style syntax. Or you could implement the method on each and provide a wrapper for the opposite choice (as done here by the rand crate).
I wondered, is it also possible (and useful / idiomatic / reasonable) to implement methods on tuples, like this:
impl<V, K> Startable for (&mut V, &K)
where
V: Vehicle,
K: VehicleKey<V>,
{
fn start(self) {
let (vehicle, _key) = self;
// we don't see immediately that `vehicle` is in fact a mutable reference
vehicle.set_started(true);
println!("{}đź’¨", vehicle.repr());
}
}
fn main() {
let mut car = Car { started: false };
let key = CarKey;
(&mut car, &key).start();
assert_eq!(car.started, true);
}
A concrete use case is, for example, the sample methods of the rand crate (as linked in my OP), or methods that are currently part of a transaction in mmtkvdb. Moving them (either the sample method of the rand crate or the methods being part of the transactions in mmtkvdb) to be free-standing functions would reduce ergonomics for the caller I guess or require more names in the top-level scope.
? I feel like the order selected is as arbitrary as which type the method belongs to. You could do the same thing as rand of course and do both, but I don't see the advantage over implementing it as a method for both types.
Implementing Foo for (&A, &B) will not collide with the implementation for (&A, &C) or (&B, &C), which may provide entirely different functionality under the same method name.
which may provide entirely different functionality under the same method name.
Different functionality implemented using the same trait? That sounds like it will add confusion, unless the trait is explicitly as nonspecific as, say, Fn is.
I think that when it is unclear, in the sense of “not yet clarified”, where a method should go, the correct answer is to use a free-standing function until you know more, and the right final answer may be to just keep it that way.