I'm writing a function that consumes self and accepts another parameter. This other parameter is a more complex struct that also contains data originating on self (copied or cloned). This doesn't work ergonomically, because the function parameters (including self) are passed in the wrong order by the language:
struct WorksOnSelf {
uses_field: bool
}
impl WorksOnSelf {
fn take_self_and_something_that_contains_own_field(self, field: bool) {}
}
fn example() {
let work_on_self = WorksOnSelf { uses_field: true };
// error: work_on_self is already moved in argument position
work_on_self.take_self_and_something_that_contains_own_field(work_on_self.uses_field);
}
I don't see anything dictating that selfmust be moved into the function before the content of the parentheses is evaluated. I may be missing something. Is it conceivable that a future version of Rust may make this compile?
One way this could be done is to allow the self parameter to be placed into any position in the parameter list...
I suppose it's "conceivable", but it seems unlikely.
Allowing functions to choose the evaluation order of their parameters only makes sense if the author of the function knows what would be more convenient for their callers. I don't think they necessarily know that. Putting self last is not always the right choice.
// taken from your original example
struct WorksOnSelf {
uses_field: bool
}
impl WorksOnSelf {
fn take_self_and_something_that_contains_own_field(self, field: bool) {}
}
// My own type
struct Builder;
impl Builder {
fn make_works_on_self(&self) -> WorksOnSelf {
WorksOnSelf { uses_field: true }
}
fn consume_and_make_bool(self) -> bool {
true
}
}
fn main() {
let builder = Builder;
builder
.make_works_on_self() // borrows `builder`
.take_self_and_something_that_contains_own_field(
builder.consume_and_make_bool() // takes `builder` by move; must go last
);
}
If take_self_and_something_that_contains_own_field evaluates its receiver after evaluating its parameter, the above example will fail. This means libraries probably shouldn't choose evaluation order on their users' behalf, and Rust probably won't provide that feature.
I think I have an idea where you're going, but I think your example doesn't actually show it, because this still works:
struct WorksOnSelf {
uses_field: bool
}
impl WorksOnSelf {
fn take_self_and_something_that_contains_own_field(self, field: bool) {}
}
// My own type
struct Builder;
impl Builder {
fn make_works_on_self(&mut self) -> WorksOnSelf {
WorksOnSelf { uses_field: true }
}
fn consume_and_make_bool(self) -> bool {
true
}
}
fn main() {
let mut builder = Builder;
builder
.make_works_on_self() // borrows `builder` now mutably, blocks any other borrow
.take_self_and_something_that_contains_own_field(
builder.consume_and_make_bool() // borrows fine. Borrow is done by this point
);
}
I think a crucial point is that in self.consume_self(self), you could do arbitary things with self in argument position (build new things would be most sensible), whereas in receiver position there is nothing you can do except passing it to the associated function. I'm therefore not so sure if there would be possibilities of conflict. Unless, of course, you consume self in the argument position, but that's a user error
That is due to an under-specified and undocumented feature called two-phased borrows. Somewhat similarly to how you want some special carve-out for taking by ownership, some had a desire for a special carve-out for &mut self methods back in the first edition days, and it got implemented. Note that the actual implementation of two-phased borrows isn't as conservative as the RFC states (hence, the actual feature being under-specified).