For parameters whose types are enums: Is it better to copy or to borrow? Or does it depend on the data that is wrapped by the enum values?
fn copy_param(param: Option<String>) {}
fn borrow_param(param: &Option<String>) {}
Related – which is better?
fn copy_param(param: Arc<String>) {}
fn borrow_param(param: &Arc<String>) {}
I’m a beginner, so take this not as gospel and with a teaspoon of grains of salt.
As with other Copy
values, it seems preferable to take the least indirection possible.
For the second one, do you need the double indirection? Arc
is already a smart pointer.
1 Like
Right. It comes down to “cost of copying” vs. “cost of following the indirection”.
Copy
means that Clone
is just memcpy
so it’ll be incredibly fast. In the end, the pointer you’re passing by borrowing will also need to be copied. I think that thinking about the cost here rather than the clarity is premature optimization.
1 Like
My reply below will mostly be about Option
.
Note that Option<String>
is by move NOT by copy, since String
isn't Copy
and thus Option<String>
isn't Copy
either. So there's not much to do with enums going on with it, but about move-vs-borrow in general.
The rules for trivial field-less enums (like cmp::Ordering
) are very different from anything involving String
.
Note that &Option<String>
is a code smell for a parameter. Is there a reason that you need the caller to have exactly an Option<String>
variable somewhere that you're borrowing?
In general, it's more flexible for parameters to be Option<&T>
instead of &Option<T>
, since then people can just pass None
.
And that's even more true for Option<String>
, since the parameter can be Option<&str>
instead -- that way I can pass Some(&blah[10..20])
to it. If that function took &Option<String>
, I'd instead have to copy those 10 bytes into a fresh String
, then put that String
into an Option
, and then pass a reference to that.
(For return types you can consider returning &Option<T>
, since people can always all .as_ref()
to turn that into Option<&T>
, but there's no free way to go the other way.)
4 Likes
Probably the former. &Arc<T>
is double indirection. Arc<T>
is already a pointer.
1 Like
Prefer Arc<T>
over &Arc<T>
only if you are expressing some kind of ownership transfer semantic.
Cloning an Arc<T>
has a non-trivial cost, unless you are sure the function need a owned Arc<T>
, pass an &Arc<T>
instead.
2 Likes
That's a good way to put it. As with all tradeoffs, "it depends".
Note that this is a specific instance of the more general rule,
If you always need an owned version of something, make your caller give you one (as opposed to clone
ing or to_owned
ing inside the function)