Reputation: 1878
Is there a way to get rid of clone()
, given the restrictions I've noted in the comments? I would really like to know if it's possible to use borrowing in this case, where modifying the third-party function signature is not possible.
// We should keep the "data" hidden from the consumer
mod le_library {
pub struct Foobar {
data: Vec<i32> // Something that doesn't implement Copy
}
impl Foobar {
pub fn new() -> Foobar {
Foobar {
data: vec![1, 2, 3],
}
}
pub fn foo(&self) -> String {
let i = third_party(self.data.clone()); // Refactor out clone?
format!("{}{}", "foo!", i)
}
}
// Can't change the signature, suppose this comes from a crate
pub fn third_party(data:Vec<i32>) -> i32 {
data[0]
}
}
use le_library::Foobar;
fn main() {
let foobar = Foobar::new();
let foo = foobar.foo();
let foo2 = foobar.foo();
println!("{}", foo);
println!("{}", foo2);
}
Upvotes: 2
Views: 370
Reputation: 127851
As long as your foo()
method accepts &self
, it is not possible, because the
pub fn third_party(data: Vec<i32>) -> i32
signature is quite unambiguous: regardless of what this third_party
function does, it's API states that it needs its own instance of Vec
, by value. This precludes using borrowing of any form, and because foo()
accepts self
by reference, you can't really do anything except for cloning.
Also, supposedly this third_party
is written without any weird unsafe hacks, so it is quite safe to assume that the Vec
which is passed into it is eventually dropped and deallocated. Therefore, unsafely creating a copy of the original Vec
without cloning it (by copying internal pointers) is out of question - you'll definitely get a use-after-free if you do it.
While your question does not state it, the fact that you want to preserve the original value of data
is kind of a natural assumption. If this assumption can be relaxed, and you're actually okay with giving the data
instance out and e.g. replacing it with an empty vector internally, then there are several things you can potentially do:
foo(&self)
to foo(&mut self)
, then you can quite easily extract data
and replace it with an empty vector.Cell
or RefCell
to store the data. This way, you can continue to use foo(&self)
, at the cost of some runtime checks when you extract the value out of a cell and replace it with some default value.Both these approaches, however, will result in you losing the original Vec
. With the given third-party API there is no way around that.
If you still can somehow influence this external API, then the best solution would be to change it to accept &[i32]
, which can easily be obtained from Vec<i32>
with borrowing.
Upvotes: 2
Reputation: 416
No, you can't get rid of the call to clone
here.
The problem here is with the third-party library. As the function third_party
is written now, it's true that it could be using an &Vec<i32>
; it doesn't require ownership, since it's just moving out a value that's Copy
. However, since the implementation is outside of your control, there's nothing preventing the person maintaining the function from changing it to take advantage of owning the Vec
. It's possible that whatever it is doing would be easier or require less memory if it were allowed to overwrite the provided memory, and the function writer is leaving the door open to do so in the future. If that's not the case, it might be worth suggesting a change to the third-party function's signature and relying on clone
in the meantime.
Upvotes: 2