4

I've got a vector of mutable references:

struct T;
let mut mut_vec: Vec<&mut T> = vec![];

How can I pass (a copy of) it into a function that takes a vector of immutable references?

fn cool_func(mut immut_vec: Vec<&T>) {}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
wizzwizz4
  • 6,140
  • 2
  • 26
  • 62
  • You can't copy nor clone mutable references, so you can't copy or clone a vector of mutable references. – mcarton Jun 25 '19 at 12:38
  • @mcarton But can you convert a mutable reference to immutable references, if the immutable reference is never used during the lifetime of the mutable references? – wizzwizz4 Jun 25 '19 at 12:46
  • 1
    Yes but that moves the mutable reference, so you wouldn't be able to keep the original vector. – mcarton Jun 25 '19 at 12:48
  • 1
    Why do (you think) you need this? Vectors of references are fishy enough as they are, but trying to have simultaneously several vectors of references to the same elements with different mutability seems weird. – mcarton Jun 25 '19 at 12:53
  • @mcarton In a loop, I'm running a function to check which resource has the most time left (the function), then subtracting the time required for a particular task from that resource (among other things). I only need this copy to pass into the function, since the function mutates the vector it's given. – wizzwizz4 Jun 25 '19 at 12:56
  • 1
    This looks like an XY problem to me. I'd recommend asking a seperate question with a more representative example of what you're trying to achieve - my hunch is that there's likely a more 'rust-y' way to approach it. – Joe Clay Jun 25 '19 at 13:08
  • @JoeClay [I've posted the "X" here](https://stackoverflow.com/q/56756713/5223757), though the title's rubbish and I'm not sure of the tags to use. – wizzwizz4 Jun 25 '19 at 14:49

2 Answers2

5

You can dereference and reborrow the mutable references, then add them to a new Vec:

fn main() {
    let mut st = String::new();

    let mut_vec = vec![&mut st];
    let immut_vec = mut_vec.into_iter().map(|x| &*x).collect();

    cool_func(immut_vec);
}

fn cool_func(_: Vec<&String>) {}

Note however, that this consumes the original Vec - you can't really get around this, as if the original Vec still existed, you'd have both mutable and immutable references to the same piece of data, which the compiler will not allow.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Joe Clay
  • 33,401
  • 4
  • 85
  • 85
  • Is there a way to promise the compiler that I won't use the mutable references during the lifetime of the immutable references? – wizzwizz4 Jun 25 '19 at 12:48
  • You can't have a mutable reference to something at the same time as an immutable reference. There are wrapper types that can shift enforcement of this to runtime (`RefCell`, `Mutex`, etc.), but they're something of a last resort. – Joe Clay Jun 25 '19 at 13:06
  • 1
    *you can't really get around this* — [why not](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=daa16f05da13036c8a383d99522205ac)? – Shepmaster Dec 02 '20 at 20:59
1

If you need to actually convert, see Joe Clay's answer. However, you might not need to convert in the first place!

Instead of changing the argument, change the function so that it accepts both mutable and immutable references. Here we use Borrow to abstract over both:

use std::borrow::Borrow;

fn main() {
    let mut st = String::new();

    let mut_vec = vec![&mut st];
    cool_func(mut_vec);

    let immut_vec = vec![&st];
    cool_func(immut_vec);
}

fn cool_func<S>(_: Vec<S>)
where
    S: Borrow<String>,
{
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366