1
votes

It appears in my code that a value is living longer than a reference to it, even though both are created in the same scope. I'd like to know why, and how I can adjust the lifetime of my reference.

Example 1 is accepted by the compiler...

let mut rxs: Vec<Receiver<String>> = Vec::new();
let mut txs: Vec<SyncSender<String>> = Vec::new();
for _ in 0..N {
    let (tx, rx) = sync_channel(0);
    txs.push(tx);
    rxs.push(rx);
}

But Example 2 isn't...

let sel = Select::new();
let mut handles: Vec<Handle<String>> = Vec::new();
let mut txs: Vec<SyncSender<String>> = Vec::new();
for _ in 0..N {
    let (tx, rx) = sync_channel(0);
    txs.push(tx);
    handles.push(sel.handle(&rx));
}

The compiler tells me that the reference &rx is borrowed in the last line of the for loop, but is dropped at the end of the for loop and needs to live longer, presumably because the reference is placed in a structure with longer lifetime. Why would the reference have a different lifetime than the value, and if the value can be moved into a structure as in the first example, why not a reference like in the second?

Finally, I'd like to know why I don't encounter the same issue in Example 3, even though a reference is borrowed and passed into a structure that lasts longer than the scope of the borrow...

let (txs, rxs): (Vec<SyncSender<String>>, Vec<Receiver<String>>) =
    (0..N).map(|_| sync_channel(0)).unzip();
let handles: Vec<Handle<String>> =
    rxs.iter().map(|x| sel.handle(&x)).collect();
2

2 Answers

2
votes

In the first example you are moving rx into the rxs vec. That's fine because you move the ownership of rx too, and it won't get dropped.

In the second example, you are passing a reference to sel.handle(), which is another way of saying it is being borrowed. rx is dropped at the end of each loop iteration, but handles outlives the entire loop. If the compiler didn't stop this from happening then handles would be full of dangling pointers.

But why would the reference have a different lifetime than the value

A reference always has a shorter lifetime than the value that it references. This has to be the case: the reference must exist and be allocated to memory before you can find its address. After a value is dropped, any reference to it is pointing at freed memory, which could be being used for something else.

and if the value can be moved into a structure as in the first example, why not a reference like in the second?

In the second example, the reference is being moved. But the original value isn't. The reference is now pointing at the free memory which was previously used by rx.

In the third example, you have created vectors which own all of the Senders and Receivers. As long as txs and rxs stay in scope, these values will not be dropped.

1
votes

In example 2, rx does not have the same lifetime as handles. In fact, it's dropped at the end of the loop, like this:

let sel = Select::new();
let mut handles: Vec<Handle<String>> = Vec::new();
let mut txs: Vec<SyncSender<String>> = Vec::new();
for _ in 0..N {
    let (tx, rx) = sync_channel(0);
    txs.push(tx);
    handles.push(sel.handle(&rx));
    drop(tx);
    drop(rx);
}
drop(txs);
drop(handles);
drop(sel);

Exmaple 3 is not equivalent to example 2. This is equivalent to example 2, and it fails:

let (txs, rxs): (Vec<SyncSender<String>>, Vec<Receiver<String>>) =
    (0..N).map(|_| sync_channel(0)).unzip();
let handles: Vec<Handle<String>> =
    rxs.into_iter().map(|x| sel.handle(&x)).collect(); // <-- notice: into_iter()

The iter() function returns an iterator of references. That's why this works:

let (txs, rxs): (Vec<SyncSender<String>>, Vec<Receiver<String>>) =
    (0..N).map(|_| sync_channel(0)).unzip();
let handles: Vec<Handle<String>> =
    rxs.iter().map(|x| sel.handle(x)).collect(); // <-- notice: no `&`