Hello everyone,
I have a question about the semantics of Copy
and Clone
. (If there is a better topic for this, please let me know or move it.)
Consider this trivial, rather silly program:
#[derive(Copy, Clone)]
struct Point {
x: f32,
y: f32,
}
fn euclidean_distance(p1: &Point, p2: &Point) -> f32 {
let p1 = p1.clone(); // unnecessary clone
let p2 = p2.clone(); // unnecessary clone
((p2.x - p1.x).powf(2.0) + (p2.y - p1.y).powf(2.0)).sqrt()
}
fn main() {
let p1 = Point { x: 10.0, y: 1.0 };
let p2 = Point { x: 1.0, y: 1.0 };
println!("The distance is {}", euclidean_distance(&p1, &p2));
}
Silly though it is, I have similar code repeated over 150 times in a code base, because it is the output of a procedural macro.
Imagine that euclidean_distance
is generated on any type defined by macro inputs. That type may or may not be Copy
, but it will always be Clone
.
The generated function always takes references, because sometimes it can avoid copies. But when it needs copies, it must clone()
them.
Now that I'm starting to use clippy more, I discovered that it has a lint that fires on all my Copy types. I was surprised: wouldn't rustc optimize out Clone
on Copy
types?
Well, looking at the MIR of the example, the answer is no. The explicit call to clone is still in there, even in release mode. It even uses extra stack slots just to make the call!
Is this a missed optimization, or preserving a language semantic? In other words: does deriving Copy
always imply that Clone
is a trivial copy?