I've been reading questions like Why does a function that accepts a Box<MyType> complain of a value being moved when a function that accepts self works?, Preferable pattern for getting around the "moving out of borrowed self" checker, and How to capture self consuming variable in a struct?, and now I'm curious about the performance characteristics of consuming self but possibly returning it to the caller.
To make a simpler example, imagine I want to make a collection type that's guaranteed to be non-empty. To achieve this, the "remove" operation needs to consume the collection and optionally return itself.
struct NonEmptyCollection { ... }
impl NonEmptyCollection {
fn pop(mut self) -> Option<Self> {
if self.len() == 1 {
None
} else {
// really remove the element here
Some(self)
}
}
}
(I suppose it should return the value it removed from the list too, but it's just an example.) Now let's say I call this function:
let mut c = NonEmptyCollection::new(...);
if let Some(new_c) = c.pop() {
c = new_c
} else {
// never use c again
}
What actually happens to the memory of the object? What if I have some code like:
let mut opt: Option<NonEmptyCollection> = Some(NonEmptyCollection::new(...));
opt = opt.take().pop();
The function's signature can't guarantee that the returned object is actually the same one, so what optimizations are possible? Does something like the C++ return value optimization apply, allowing the returned object to be "constructed" in the same memory it was in before? If I have the choice between an interface like the above, and an interface where the caller has to deal with the lifetime:
enum PopResult {
StillValid,
Dead
};
impl NonEmptyCollection {
fn pop(&mut self) -> PopResult {
// really remove the element
if self.len() == 0 { PopResult::Dead } else { PopResult::StillValid }
}
}
is there ever a reason to choose this dirtier interface for performance reasons? In the answer to the second example I linked, trentcl recommends storing Option
s in a data structure to allow the caller to do a change in-place instead of doing remove
followed by insert
every time. Would this dirty interface be a faster alternative?
self
as compared to any other variable of any type. – Shepmaster