Rustlings: how to generalize AsMut for Box<T>?

I'm working my way through Rustlings 5.2.1 in conversions/as_mut_ref.rs, so

Spoiler Alert!

The current version of this exercise asks us to add a trait bound and implement a function that takes a mutable reference to a number and squares the number in-place. Here's the test that runs the function num_sq():

    #[test]
    fn mult_box() {
        let mut num: Box<u32> = Box::new(3);
        num_sq(&mut num);
        assert_eq!(*num, 9);
    }

A simple solution for num_sq() would be this, but it's limited to u32:

fn num_sq<T: AsMut<u32>>(arg: &mut T) {
    *arg.as_mut() *= *arg.as_mut();
}

I'm trying to figure out how to generalize the generic function to work with any numeric type that allows multiplication and assignment.

These implementations do not work, mostly because the compiler can't tell from them how to handle the Box<u32>:

fn num_sq<T>(arg: &mut T) where T: AsMut<T> + Copy + std::ops::MulAssign {
    *arg.as_mut() *= *arg.as_mut();
}
fn num_sq<T: AsMut<T>>(arg: &mut T) {
    num_sq_inner(&mut *arg.as_mut())
}
fn num_sq_inner<T>(arg: &mut T) where T: std::ops::MulAssign {
    *arg *= *arg;
}

It seems like I somehow need to tell the compiler "here's how to handle Box and here's how to square T assuming it supports multiplication and assignment," but I'm not sure how to express that in Rust's syntax.

Is there a way to generalize the generic num_sq()?

Here's a link to the code in the playground.

Thanks in advance!

T: AsMut<T> is your problem. In your concrete test T is Box, which doesn't have anything to do with multiplication. You need another type parameter to represent the numeric type

Playground

fn num_sq<T, U>(arg: &mut T)
where
    T: AsMut<U>,
    U: Copy + std::ops::MulAssign,
{
    let value = *arg.as_mut();
    *arg.as_mut() *= value;
}

#[test]
fn mult_box() {
    let mut num: Box<u32> = Box::new(3);
    num_sq(&mut num);
    assert_eq!(*num, 9);
}
2 Likes

Ah! That makes sense. Thank you!

I ended up with this, using Mul and no temporary variable.

fn num_sq<T, U>(arg: &mut T)
where
    T: AsMut<U>,
    U: std::ops::Mul<Output = U> + Copy,
{
    //    as_mut will give a ref if mut is not needed
    //    then we can use it twice in a block
    //    the references are released before
    //    the as_mut mutable reference is used.
    *arg.as_mut() = {
        *arg.as_mut() * *arg.as_mut()
    }
}

Just because I wanted to do it with Mul, not MulAssign

Thanks! I was curious how the compiler would handle the two different implementations. The unoptimized assembly language is pretty similar, and basically what you'd expect: load parameters and then either call <u32 as core::ops::arith::MulAssign>::mul_assign or <u32 as core::ops::arith::Mul>::mul followed by a manual assignment.

LOL, thank but my code was pretty wrong. The comments and extra block anyway.

fn num_sq<T, U>(arg: &mut T)
where
    T: AsMut<U> + std::fmt::Debug,
    U: std::ops::Mul<Output = U> + Copy,
{
    *arg.as_mut() = *arg.as_mut() * *arg.as_mut();
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.