in a normal fn, both ref and mut ref will not moved out;
in a template fn, mut ref will be moved out
in a template fn, ref will not be moved out
in a fn which does not moving out mut ref argments, it can return another mut ref; both seems alive but the before the returning one gone, you can't access the older one
Surely, if you accept all of these and take it as natural, I disagree with you.
It's natural for normal person that: rules should keep simple, and should not have exceptions?
Right, ok, I see the issue now. First, let's cover mutable references. Those are guaranteed to be unique, and to provide a mutable reference to something, you must reborrow it, which leaves the original mutable reference inaccessible until the reborrowed mutable reference is no longer used.
The syntax for reborrowing a mutable reference is &mut *xref. However a mutable reference is automatically reborrowed when no generics are involved. Your test3 consumes the mutable reference because there are generics.
You can explicitly reborrow it, and it will work:
test3(&mut *xref);
As for immutable references, those are not guaranteed to be unique, so they are copied instead of reborrowed, hence the issues go away.
There’s a tradeoff here that makes it hard to make these kinds of decisions: most of the time, the exceptions are there because they’re the most common case and you don’t want to clutter up all those callsites with line noise. On the flip side of this equation is the extra learning difficulty when exceptions are present.
You can see the same sort of thing in natural languages: the irregular verbs always tend to be the extremely common ones, like “to be”.
Hmm, it's an interesting point. A mutable reference does not implement the Copy trait, but still sometimes behaves like one when it is automatically reborrowed. On the contrary, all other non-Copy types are moved when given to a function.
Can you elaborate on that? The rules are simple: by-value arguments move, by-reference arguments don't. What exactly is the confusing bit?
As for mutable references — indeed, they are sometimes confusing even for the designers of the language (the standard library has had mutable aliasing bugs before IIRC), but that's because the rules are necessary to eliminate mutable aliasing in safe code.
For me, simple and no exceptions rule is most important, because it leads a trust-able and clear result.
More than 100 pages of rules consist of "in case of ..." is un-acceptable.
Especially the rules come to you sight only after it has surprised you.
There is a example code above in play. You can check it to see what happened.
And all the complicated rules are not necessary for safe code.
Obviously, all arguments moved out -- no matter they are references or not -- is not dangerous.
The only problem is :
when passes a reference to a sub-function and the caller want to use the reference after that, it needs to re-generate the reference.
which is not complicated in my preferred solution.
fn move_it_out(r: &String) {}
fn prefer(r: &String) {
let p = r as *const String;
move_it_out(r);
let r = unsafe{&(*p)};
// continue use r
}
&mut Timplements Display, but is apparently not automatically reborrowed when passed as an argument to this function. The net result is that calling test3 will consume the reference, making it inaccessible to subsequent code.
This is expected behavior for non-Copy types in general, but surprising for &muts which usually get automatically reborrowed in similar situations.
I really have no idea what you are trying to say with this snippet.
fn move_it_out(r: &String) {}
fn prefer(r: &String) {
let p = r as *const String;
move_it_out(r);
let r = unsafe{&(*p)};
// continue use r
}
Your move_it_out function does not do anything, and does not consume the reference, as immutable references are Copy, which means that they cannot be consumed.
The raw pointers also do not seem to do anything useful here.