Is it possible to return a struct with references to mutable data in it in Rust?

86 Views Asked by At

I was making a little pseudo-game thing and wanted to create a function to return a generic enemy. For some reason it didn't allow me to include use of [0, 0]. Here's the code:

struct Enemy<'a> {
    pos: &'a mut [i32; 2],// [0] = x, [1] = y
    veloc: &'a mut [i32; 2],// see `pos` property
    monstype: String,
    id: String
}

impl<'a> Enemy<'_> {
    pub fn new(monster_type: String) -> Enemy<'a> {
        Enemy {
            pos: &mut [0, 0],
            veloc: &mut [0, 0],
            monstype: monster_type,
            id: Enemy::generate_id(8)// unrelated, omitted from this code snippet
        }
    }
}

This, generates this error:

error[E0515]: cannot return value referencing temporary value
  --> src\main.rs:19:9
   |
19 | /         Enemy {
20 | |             pos: &mut [0, 0],
21 | |             veloc: &mut [0, 0],
   | |                         ------ temporary value created here
22 | |             monstype: monster_type,
23 | |             id: Enemy::generate_id(8)
24 | |         }
   | |_________^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing temporary value
  --> src\main.rs:19:9
   |
19 | /         Enemy {
20 | |             pos: &mut [0, 0],
   | |                       ------ temporary value created here
21 | |             veloc: &mut [0, 0],
22 | |             monstype: monster_type,
23 | |             id: Enemy::generate_id(8)
24 | |         }
   | |_________^ returns a value referencing data owned by the current function

I tried .clone() on the array but that of course doesn't work as the data is still temporary and owned by the function.

1

There are 1 best solutions below

4
Silvio Mayolo On

I think you're probably looking for

struct Enemy {
    pos: [i32; 2],
    veloc: [i32; 2],
    monstype: String,
    id: String,
}

There's no reason (at least, no reason in the snippet you've shown) for Enemy to be borrowing anything, so we can cut out the lifetime parameter 'a and all of the borrow complexity and just return arrays. Arrays are perfectly valid sized data-types and can be returned as part of a struct.

If you don't know the size in advance, you can use Vec.

struct Enemy {
    pos: Vec<i32>,
    veloc: Vec<i32>,
    monstype: String,
    id: String,
}

Then you can construct these using the ever-helpful vec macro.

Enemy {
  pos: vec![0, 0],
  veloc: vec![0, 0],
  monstype: String::from("Whatever"),
  id: String::from("0"),
}

Finally, if your arrays are large and you have good reason to be worried about passing them by value, then you can add indirection not with &mut (which is for borrows) but with Box. I do not recommend doing this unless you have very good reason to. The extra indirection is very likely to slow your program down in most cases. But you can do

struct Enemy {
    pos: Box<[i32; 2]>,
    veloc: Box<[i32; 2]>,
    monstype: String,
    id: String,
}

Then construct as

Enemy {
  pos: Box::new([0, 0]),
  veloc: Box::new([0, 0]),
  monstype: String::from("Whatever"),
  id: String::from("0"),
}