Hello everyone,
I have a question about the semantics of Copy
and Clone
.
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. Here's why.
With the help of a macro, 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 for consistency. (The real function is much more complex, of course, and may not need to make copies.) As a result, when I need owned copies, I must clone()
them.
Now that I'm starting to use clippy more, I discovered that it has a lint for that. 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?