The function below works, but it consumes its arguments. Is there some way to write an operator function that just borrows its arguments? Preferably without having to write
There may be a nice way to do this, by viewing it as an optimization. See playground.
This is the 4x4 matrix multiply. But now the matrix type has a derived "Copy". That allows pass by value, so the operator doesn't consume its arguments, and, as seen here, one of them is reused. If "Copy" is not available, the error
error[E0382]: use of moved value: `m1`
is reported. That gives us ergonomics for the matrix multiply operator, at a cost in performance for the copy.
Or is the compiler smart enough to detect that it doesn't need to copy the function arguments here? If the function doesn't modify or return an argument, it need not be copied. It's the same check as for an immutable borrow. If an immutable borrow would be allowed, the copy can be optimized out. Is that being done already?
If that's being done already, just tell users it's being done and the problem is solved.
Officially saying that an optimization is done is committing to guaranteeing and maintaining that optimization, and I doubt the compiler team is willing to sacrifice the flexibility to make such a commitment -- or even able to reasonably make such a commitment, as it probably depends on LLVM for most people, or Cranelift, or whatever backend is used.
So the standard advice is "measure and see if it even matters". And/or "check the output to see if the optimization is done". And if it matters, get it into the state you want today, and write some tests to help ensure it stays that way. Sometimes #[inline] or its variants can help. Other times, you think they'll help but they hurt. And sometimes you take the ergonomic hit for the sake of performance or design.
As for your specific example, it inlined identity() a few times when I tried it, instead of reusing the results of identity(). If I put #[inline(never)] on identity(), it calls identity() once and reuses the results. If I put #[inline(always)] on the multiplication method instead, the results of the multiplications get calculated statically and baked into the binary, and the main function just prints that. But results will certainly vary call site to call site (and across compiler versions, target architectures, etc.).
It's like tail call recursion, I guess. Rust doesn't guarantee that, either.
The example just uses identity() because I needed something to generate a dummy matrix for an example. That's not a good test for optimizations, since it's a constant.