Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question related to trait bounds in excercise "as_ref_mut.rs" #1823

Open
eduarddejong opened this issue Jan 14, 2024 · 0 comments
Open

Question related to trait bounds in excercise "as_ref_mut.rs" #1823

eduarddejong opened this issue Jan 14, 2024 · 0 comments

Comments

@eduarddejong
Copy link

eduarddejong commented Jan 14, 2024

Note:
This is not a bug or issue, although it can still be considered a good idea to further explain certain things (in the hint text?).

Hi.
Generally the way I play around the Rustlings exercises is by experimenting with those a lot further than just the single thing officially asked for to solve. Simply because I like to learn and understand as much as possible.
And in this way I now noticed something that I believe is really worth asking an explanation about.

So in as_ref_mut.rs I filled in the AsMut trait bound with u32 as a type parameter for this function and I added the multiply implementation:

// Squares a number using as_mut().
// TODO: Add the appropriate trait bound.
fn num_sq<T: AsMut<u32>>(arg: &mut T) {
    // TODO: Implement the function body.
    *arg.as_mut() *= *arg.as_mut();
}

But in my opion, this could be done far better, by making the function even more generic, and replaced the hard u32 bound by the more generic MulAssign + Copy, so that this function is allowed to work with many numeric types:

// Squares a number using as_mut().
// TODO: Add the appropriate trait bound.
fn num_sq<T: AsMut<impl MulAssign + Copy>>(arg: &mut T) {
    // TODO: Implement the function body.
    *arg.as_mut() *= *arg.as_mut();
}

However, interestingly enough, this does not compile ...

error[E0499]: cannot borrow `*arg` as mutable more than once at a time
  --> exercises/conversions/as_ref_mut.rs:30:23
   |
30 |     *arg.as_mut() *= *arg.as_mut();
   |     ------------------^^^---------
   |     ||                |
   |     ||                second mutable borrow occurs here
   |     |first mutable borrow occurs here
   |     first borrow later used here
   |
help: try adding a local storing this...
  --> exercises/conversions/as_ref_mut.rs:30:23
   |
30 |     *arg.as_mut() *= *arg.as_mut();
   |                       ^^^^^^^^^^^^
help: ...and then using that local here
  --> exercises/conversions/as_ref_mut.rs:30:5
   |
30 |     *arg.as_mut() *= *arg.as_mut();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I decided to follow the compiler's advice and created an extra variable first, explicitly borrowing the thing just 1 time.
Turns out, it works, this code succeeds:

// Squares a number using as_mut().
// TODO: Add the appropriate trait bound.
fn num_sq<T: AsMut<impl MulAssign + Copy>>(arg: &mut T) {
    // TODO: Implement the function body.
    let arg = arg.as_mut();
    *arg *= *arg;
}

Alright.
But how is this possible? Why does the compiler accept a duplicate as_mut() borrow in the first case where I am directly working with u32 types only, but not when I am working via a MulAssign + Copy trait bound?
What am I missing? Is there another trait bound that I don't know about? Or is this some intrinsic compiler behaviour for numeric types?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant