The &mut *v is the reborrow: you're making a brand-new &mut that points to the same place as the old one, and the borrow checker prevents you from using the original until the new one you made is destroyed.
My understanding is: This happens because reborrowing is a coercion, and for coercions to happen the source and target types need to be known. When passing something as an argument, and the argument is generic, the target type is not known.
Rust type checks before monomorphization, and this is intentional, not a "bug". The reason for it is that once a generic function type checks, it's guaranteed to compile (and work as intended) for all possible instantiations.
If type checking depended on the monomorphized code, it would be possible to write a generic function that would compile at first, but it would be rejected for certain instantiations that otherwise satisfy the trait bounds and other constraints. This happens all the time with C++ templates, for example, and it is highly undesirable because it makes generic code a lot less robust, testing harder, and basically involves testing a bunch of different instantiations "exhaustively" in order to ensure a clean compile.
I am curious about that why it need analyse the generic code or monomorphized code. It just need to add re-borrowing when calls that function, like what has been done for a typing-by-hand normal function.
In the caller, the compiler knows whether or not to insert a reborrow before actually running the borrow checker. Here's a post I wrote a while ago on a related topic. Reborrowing can only happen at a coercion site where both the actual type A and the expected type E are of the form &'_ mut T, with (potentially) different lifetimes in the '_ slot. If E is generic, the compiler will simply choose E = A, and the value will be moved instead of reborrowed. This matches how other types of coercions work, and is why you don't have to specify types at every possible coercion site. The compiler will not choose E as some other type than A in order to allow a reborrow to happen.
In your earlier example you can make the test1 call compile by writing test3::<&mut _>(xref), which hints to the compiler to use a fresh lifetime for monomorphizing test3, so xref will be reborrowed instead of moved. (Of course you could also just explicitly reborrow: test3(&mut *xref).)
Could we invent a new rule that allows reborrowing everywhere? Maybe (I'm not convinced there isn't some scenario where you need to move). But that would be more complicated than the current situation, not less. Would the language be just as expressive and less complicated without the implicit reborrow rule? Yeah. But it would mean you have to use &mut * a lot and that would also be hard to explain to new users. The reborrowing rule makes sense in the vast majority of cases. Anyway, that would be a different language. (If you're inventing a new language based on Rust but without implicit reborrowing, consider also eliminating . for method call syntax; it makes things way simpler.)
tl;dr Yes, implicit reborrowing is a little quirky in how it interacts with generics, but it's useful in other ways.
It's a (current) limitation of type inference and dropck the module in the compiler that checks for ownership and moves, I'll call it moveck: Rust special cases some ownership / move rules for references, but in a generic / inferred type context, Rust may be unable to spot that it is dealing with references, thus not applying the special-cased rules
if you feed a mut_ref: &'lifetime mut T to a function such as drop::<_>, moveck seems to be triggered before inference is resolved, thus stating that the value has been moved, and thus that the original binding is invalidated unless it is Copy.
But if you feed such mut_ref to drop::<&'_ mut _>, dropck will see that even though the generics have not been inferred yet, this is dealing with a &mut ref, and thus a reborrow applies.
At that point, the only way for mut_ref to become unusable is if it gets reborrowed for exactly its own lifetime (but that's for borrowck to deny, if that were the case, after the type inference pass).
But in this case, since there is no constraint on the lifetime parameter of that drop function, inference can choose
_ = T,
and instead of '_ = 'lifetime, a special rule for lifetime inference gets to kick in, yielding: '_ = 'x where 'lifetime : 'x
hence allowing a (shorter) reborrow ('x being the lifetime of the reborrow).
This whole conversation has become so convoluted that I have long since lost track of what the problem is.
To my naive newbie Rust mind, and from my experience in C, C++ and other languages, the lifetime of anything is not determined by any tick mark decorations you put on types and declarations etc. It's determined by where it is created, where it is destroyed and how it is passed around. Typically some scope unless 'smart' pointers are involved.
As such, lifetime tick marks would be everywhere if one wants to specify types rigorously.
Luckily Rust can infer lifetimes in many cases so we don't have to have that lifetime tick mark syntactic noise everywhere.
Sometimes though Rust cannot guess what we are thinking. For whatever reason. If I understand correctly the Rust borrow checker has been getting smarter about this over time.
Well, OK, spell it out. It's not a bug. It's a bonus whichever way you look at it.
Yeah, dropck may not be the name of the module at play here, it would be something like moveck, but I don't know the official name. I remember that some ownership-related features are handled by dropck, wven if not all of them are about drops per se.
I will edit my post to remove that unclear part anyways