In the following case, I would say "only implement From<Src>
, not From<&Src>
":
// These types are NOT Copy! (Nor should they be.)
#[derive(Clone, ...)]
struct Src {
v: Vec<u8>,
}
#[derive(Clone, ...)]
struct Dst {
v: Vec<u8>,
}
My rationale: Suppose someone has a &Src
, and they want a Dst
. That can be done very easily without From<&Src>
:
Dst::from(src_ref.clone())
You might say, "But you are forcing the user to clone!". True, but you would need to clone at some point anyway:
impl From<&Src> for Dst {
fn from(src: &Src) -> Dst {
Dst {
v: src.v.clone(),
}
}
}
Alternatively,
impl From<&Src> for Dst {
fn from(src: &Src) -> Dst {
Dst::from(src.clone())
}
}
Either way, you haven't avoided cloning.
Thm 1: if you have a &Src
and you want to make a Dst
, you will ALWAYS clone at some point. Implementing From<&Src>
does not save you from that inevitability.
At the same time, you have nearly duplicated code. Look back at your From<Src>
:
impl From<Src> for Dst {
fn from(src: Src) -> Dst {
Dst {
v: src.v,
}
}
}
Looks really similar to the first impl of From<&Src>
, right?
If we did the other way around (i.e. implement From<Src>
in terms of From<&Src>
), then we would always clone (this is just a corollary of thm 1).
Whereas, if we just implement From<Src>
, the caller MIGHT be required to clone, but they might also have the opportunity to hand over ownership of Src
, which would result in just a move instead of a clone.
Ok, but what if the caller has a Src
, not a &Src
. There are two subcases here:
- The caller wants to hang onto their copy of Src.
- The caller is ok with handing over ownership to Dst::from.
Of course, in the latter case, they should use our From<Src>
.
But what about case 1? If we stick with my advice, it seems we again force the caller to clone:
Dst::from(src.clone())
If only we could do
Dst::from(&src)
! But look back to thm 1: we haven't actually avoided cloning. We just let From<&Src>
do the dirty work of cloning for us. All we have done is swepted the clone under the rug, so to speak.
I think that covers all the (interesting) cases. I didn't really cover the case where Src and Dst are Copy, because that's not really "interesting". That's "not interesting" because there are no expensive clones to get rid of. The only thing you might have to do is use the dereference operator:
Dst::from(*src_ref);
But that's not a performance issue. The only "expense" of doing that is typing one more *
character, which really won't kill you.
The main "problem" with my advice is that people will look at your clones and think, "I know how to get rid of this! I'll just add impl From<&Src>
!". But it turns out, this is a fools errand.