Taking ownership vs borrowing in public APIs

When designing Rust API's I find myself virtually always borrowing parameters rather than taking ownership, except in cases where the semantics of an API demands that ownership be taken.

I went through a phase where I implemented the Borrow trait for lots of parameters to allow the caller to choose whether to lend or hand over ownership, but I ran into some situations where it made things more complicated.

Basically what's causing me some minor grief is the following situation: A function/method could take ownership; there isn't much reason for it not to, but it doesn't require it. However, someone might want to log something after the call:

something::do_stuff(&params)?;
trace!("Call to do_stuff({:?}) was successful", params);

Just to be clear, I'm talking specifically about cases where there's no possible use for params beyond the do_stuff() call beyond logging ... and I can't help make it borrow to cover this case. Yet something in the back of my mind tells me that if something really has no further use, with regards to the API itself, then handing over ownership should be preferred.

How do people reason about these things? Should one prefer borrows or ownership transfers in cases where both are equally viable? (I.e. assume the types are trivially small).

If my method doesn't require ownership for semantic or technical reasons then I won't require them to pass by value.

That gives the caller control over exactly when their params will be destroyed, which is almost always more preferable because the caller knows a lot more about how they want to use params than you will.

If you accept a borrow when the value will never be used again then that just means it'll be implicitly destroyed when the caller returns.

However, if you force users to transfer ownership and they still want to use the value after your function exits then they are in a bit of a pickle which may or may not be possible to get out of (e.g. because the value isn't Clone).

I beg to differ. Clippy even has a lint for detecting when you unnecessarily pass by value when a plain reference could be used.

I've been using Rust for about 4 or 5 years now and I think I can count the number of times I've needed std::borrow::Borrow on one hand.

99% of the time you can just get away with normal references and not needing to mention Borrow, and that other 1% of the time is when you call HashMap::get().

3 Likes

I agree with @Michael-F-Bryan here. You should only pass ownership if passing ownership is necessary for some reason. Methods that take a value by reference are strictly more powerful than those that take by ownership.

The only exception I can think of is small Copy types for which people like to use ownership on every method, but ownership is not really that meaningful for types that are Copy anyway.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.