Reputation: 1898
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();
Upvotes: 1
Views: 95
Reputation: 668
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 `&`
Upvotes: 1
Reputation: 58865
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 Sender
s and Receiver
s. As long as txs
and rxs
stay in scope, these values will not be dropped.
Upvotes: 2