Rust parameter assignment

#1

Hi all,

The below is my Rust program. I thought the return value of function call should be [0,0,0]. But it is actually [2,2,2].

I don’t understand what goes wrong with my code. Does anyone have any clue? Thank for any comment.

// Return a new array 
fn func(b: &mut [i32;3], num: i32) -> [i32;3]{
    if num > 10 {
        b[0] = num;
    }else{
        let mut c: [i32; 3] = [0;3]; // [0,0,0]
        let b = &mut c;
    }
    return *b;
}


fn main() {
    println!("Hello, world!");
    let mut b: [i32; 3] = [2; 3]; // [2,2,2]
    let tmp = func(&mut b, 9); 
    // This loop prints: 0 0 0
    for x in &tmp {
        print!("{} ", x);
    }
    assert_eq!(tmp, [0,0,0]);
}

(Playground)

Output:

Hello, world!
2 2 2 

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `b`
 --> src/main.rs:7:13
  |
7 |         let b = &mut c;
  |             ^ help: consider using `_b` instead
  |
  = note: #[warn(unused_variables)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 0.67s
     Running `target/debug/playground`
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[2, 2, 2]`,
 right: `[0, 0, 0]`', src/main.rs:21:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

#2

You’ve declared a completely new variable called b that you never use. Defining this does absolutely nothing to change the contents of the b parameter. I’m not sure why you’re trying to use let here when a simple assignment works just fine:

let c: [i32; 3] = [0;3]; // [0,0,0]
*b = c;
1 Like
#3

Hi Daniel,

Thanks for your help. I thought we need to use move semantics to change the value of parameter b. I am not sure if using direct assignment would lead to dangle pointers.

Thanks.

#4

A few points:

  • You cannot create dangling pointers in Rust without using unsafe code. A major selling point of Rust is that you don’t have to worry about this.

  • let statements have nothing to do with move semantics. They just define variables.

  • You’re not changing b, you’re changing the thing b points to.

  • You don’t ever need to distinguish between move and copy semantics when you’re using something. If you try to copy something that can’t be copied, you’ll get a compile error. If you try to move something which cannot be moved (because there are existing pointers to it), you’ll get a compile error. Again, this is not something you ever need to worry about.

4 Likes
#5

Hi Daniel,

We want to make a copy of parameter ‘b’ and pass it to ‘func’, so that we can make sure array ‘b’ will not be changed by ‘func’.

// Return a new array 
fn func(b: &mut [i32;3], num: i32) -> [i32;3]{
    if num > 10 {
        b[0] = num;
    }else{
        let c: [i32; 3] = [0;3]; // [0,0,0]
        *b = c;
    }
    return *b;
}


fn main() {
    println!("Hello, world!");
    let mut b: [i32; 3] = [2; 3]; // [2,2,2]
    let tmp = func(&mut b, 9); 
    assert_eq!(b, [2,2,2]);
    // This loop prints: 0 0 0
    for x in &tmp {
        print!("{} ", x);
    }
    assert_eq!(tmp, [0,0,0]);
}

(Playground)

What should we do? Thanks

#6

By default, unless you specify mut, the array b cannot be changed in the function. So you could remove the mut from the signature of the function, and it would not be allowed to change the array.

If you still want the copy (to be able to make changes), types which support this type of copying implement Clone, so you could write let newArray = b.clone();. This would make a copy of b, and changes to it would not affect b.

#7

Use &mut only when you want to allow functions to change the referenced values. Use & to disallow changing of the referenced data. Don’t use & when you want a copy.

#8

Hi all,

I tried to change it but got the error:

// Return a new array 
fn func(b: [i32;3], num: i32) -> [i32;3]{
    if num > 10 {
        b[0] = num;
    }else{
        let c: [i32; 3] = [0;3]; // [0,0,0]
        b = c;
    }
    return b;
}


fn main() {
    println!("Hello, world!");
    let b: [i32; 3] = [2; 3]; // [2,2,2]
    let tmp = func(b, 9); 
    assert_eq!(b, [2,2,2]);
    // This loop prints: 0 0 0
    for x in &tmp {
        print!("{} ", x);
    }
    assert_eq!(tmp, [0,0,0]);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0594]: cannot assign to `b[..]`, as `b` is not declared as mutable
 --> src/main.rs:4:9
  |
2 | fn func(b: [i32;3], num: i32) -> [i32;3]{
  |         - help: consider changing this to be mutable: `mut b`
3 |     if num > 10 {
4 |         b[0] = num;
  |         ^^^^^^^^^^ cannot assign

error[E0384]: cannot assign to immutable argument `b`
 --> src/main.rs:7:9
  |
2 | fn func(b: [i32;3], num: i32) -> [i32;3]{
  |         - help: make this binding mutable: `mut b`
...
7 |         b = c;
  |         ^^^^^ cannot assign to immutable argument

error: aborting due to 2 previous errors

Some errors occurred: E0384, E0594.
For more information about an error, try `rustc --explain E0384`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

#9

It’s worth also talking about Copy. Some types, like the primitive numeric types, are marked with Copy - this makes it so that they are copied when not using &, as @kornel says. This happens instead of ‘moving’ the value from one owner to another.

#10

The error message already contains the right solution to this problem:

#11

Here is a link to the playground, where I’ve made the change I suggested. Read it, and see if you understand what I did. If not, feel free to keep asking here.

Because arrays inherit their Copy- and Clone- ability from the type they’re declared from, @kornel’s solution of simply using an ownership-transferring assignment does work here. However, it might not, depending on what is in the array. My solution might not, either - if they type is not Clone-able, then you simply cannot make a copy so simply.

For a more detailed look at what we’re talking about, you may want to read The Book - Especially the chapter here talking about borrowing and ownership. They’re pretty important in rust, and one of the things rust does differently from most languages

#12

Hi Zarenor,

I tried to the Rust link (https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7e84643cba2e6f27a0807948e4454e3f), but it has errors.

#13

Sorry. I’ve updated the link above. It’s also here.

2 Likes