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?