re.captures error: borrowed value does not live long enough - rust

Trying to complete the "Hash Maps" chapter of the Rust book at https://doc.rust-lang.org/book/2018-edition/ch08-03-hash-maps.html , with this code:
extern crate regex;
use std::collections::HashMap;
use std::io;
use regex::Regex;
fn get_command() -> String {
let mut input_cmd = String::new();
io::stdin().read_line(&mut input_cmd)
.expect("Failed to read command");
let input_cmd = input_cmd.trim();
input_cmd.to_string()
}
fn main() {
println!("Add someone by typing e.g. \"Add Sally to Engineering\", list everyone in a department by typing e.g. \"List everyone in Sales\", or list everyone by typing \"List everyone\". To quit, type \"Quit\".");
let mut employees_by_dept: HashMap<&str, Vec<&str>> = HashMap::new();
let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
let list_all_re = Regex::new("^List everyone$").unwrap();
loop {
let input_cmd = get_command();
let caps = add_to_dept_re.captures(&input_cmd).unwrap();
if add_to_dept_re.is_match(&input_cmd) {
let dept_name = caps.get(2).unwrap().as_str();
let employee_name = caps.get(1).unwrap().as_str();
println!("Adding person");
employees_by_dept.entry(&dept_name)
.or_insert_with(Vec::new)
.push(employee_name);
} else if list_in_dept_re.is_match(&input_cmd) {
println!("Listing people");
} else if list_all_re.is_match(&input_cmd) {
println!("Listing everyone");
} else if input_cmd == "Quit" {
break;
} else {
println!("Invalid command");
break;
}
}
println!("Bye!");
}
But I get this:
error[E0597]: `input_cmd` does not live long enough
--> src/main.rs:28:45
|
28 | let caps = add_to_dept_re.captures(&input_cmd).unwrap();
| ^^^^^^^^^ borrowed value does not live long enough
...
48 | }
| - `input_cmd` dropped here while still borrowed
...
51 | }
| - borrowed value needs to live until here
Have tried .captures(&input_cmd.clone()) and various other things, but doesn't help. Any ideas?

Rust memory safety rules prevents this type of approach: your HashMap value outlives the inserted items.
See embedded comments below but especially the Ownership chapter of the book.
fn main() {
let mut employees_by_dept: HashMap<&str, Vec<&str>> = HashMap::new();
let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
let list_all_re = Regex::new("^List everyone$").unwrap();
loop {
let input_cmd = get_command();
let caps = add_to_dept_re.captures(&input_cmd).unwrap();// <--- input_cmd
//is borrowed here
// ... code for getting dept_name and employee_name references
// and inserting into HashMap omitted
} // <----- The String input_cmd is dropped here (memory is freed)
// this implies that dept_name and employee_name references
// points to deallocated memory
// ... At this point you will have a live employees_by_dept HashMap
// that contains references to deallocated memory
println!("Bye!");
}
Make instead the HashMap take ownership of the keys/items values:
fn main() {
println!("Add someone by typing e.g. \"Add Sally to Engineering\", list everyone in a department by typing e.g. \"List everyone in Sales\", or list everyone by typing \"List everyone\". To quit, type \"Quit\".");
let mut employees_by_dept: HashMap<String, Vec<String>> = HashMap::new();
let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
let list_all_re = Regex::new("^List everyone$").unwrap();
loop {
let input_cmd = get_command();
let caps = add_to_dept_re.captures(&input_cmd).unwrap();
if add_to_dept_re.is_match(&input_cmd) {
let dept_name = caps.get(2).unwrap().as_str();
let employee_name = caps.get(1).unwrap().as_str();
println!("Adding person");
employees_by_dept
.entry(dept_name.to_string())
.or_insert_with(Vec::new)
.push(employee_name.to_string());
} else if list_in_dept_re.is_match(&input_cmd) {
println!("Listing people");
} else if list_all_re.is_match(&input_cmd) {
println!("Listing everyone");
} else if input_cmd == "Quit" {
break;
} else {
println!("Invalid command");
break;
}
}
println!("Bye!");
}

Related

Why is Rust still borrowing after a match statement?

I have the following code:
use std::collections::HashMap;
type Elem = i64;
enum Op {
Num(Elem),
Add((String, String)),
}
type Statements = HashMap<String, Op>;
fn main() {
let mut statements = Statements::new();
statements.insert("lhs".to_string(), Op::Num(64));
statements.insert("op".to_string(), Op::Add(("lhs".to_string(), "rhs".to_string())));
statements.insert("rhs".to_string(), Op::Num(64));
let mut stack = Vec::new();
let mut op_name = "op";
loop {
let result: Elem;
match statements.get(op_name).unwrap() {
Op::Num(_) => {
panic!("{}: Did not expect Num", op_name);
},
Op::Add((lhs, rhs)) => {
if let Op::Num(lhs_value) = statements.get(lhs).unwrap() {
if let Op::Num(rhs_value) = statements.get(rhs).unwrap() {
result = lhs_value + rhs_value;
} else {
stack.push(op_name);
op_name = rhs;
continue;
}
} else {
stack.push(op_name);
op_name = lhs;
continue;
}
},
};
if let Some(new_op_name) = stack.pop() {
*(statements.get_mut(op_name).unwrap()) = Op::Num(result);
op_name = new_op_name;
} else {
println!("Result: {}", result);
return;
}
}
}
which gives me the following compiler error:
error[E0502]: cannot borrow `statements` as mutable because it is also borrowed as immutable
--> minimal.rs:43:15
|
22 | match statements.get(op_name).unwrap() {
| ----------------------- immutable borrow occurs here
...
43 | *(statements.get_mut(op_name).unwrap()) = Op::Num(result);
| ^^^^^^^^^^^-------^^^^^^^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
I was expecting the borrowing to end with the match statement, since I do not see how I could still access the result of the borrowing after this statement. Why is the value still being borrowed after the match statement?
Is there something I could do to make my code above work?
In my opinion you'll find it difficult to work with strings and strings references. I would rather switch to some type for your HashMap keys that would be small, and copyable, like an integer number. This way you'll not be required to deal with references and mutability - you can just copy your integers with little to no cost.
I resolved your compilation errors, but the overall code becomes more convoluted. Basically now each insert into the hash map will give you a unique integer ID of the operation. You can only query operations from the hash map using this provided ID. It also requires you to push statements in your hash map in the order of appearance. You can't insert add(lhs, rhs) unless you already know IDs of both lhs and rhs
use std::collections::HashMap;
type Elem = i64;
type StatementId = usize;
enum Op {
Num(Elem),
Add((StatementId, StatementId)),
}
#[derive(Default)]
struct StatementsRecord {
record: HashMap<StatementId, (String, Op)>,
next_statement_id: StatementId,
}
impl StatementsRecord {
fn insert(&mut self, name: String, operation: Op) -> StatementId {
self.record.insert(self.next_statement_id, (name, operation));
self.next_statement_id += 1;
self.next_statement_id - 1 // return id of newly stored operation
}
fn get(&self, id: &StatementId) -> Option<&(String, Op)> {
self.record.get(id)
}
fn get_mut(&mut self, id: &StatementId) -> Option<&mut (String, Op)> {
self.record.get_mut(id)
}
fn update(&mut self, id: &StatementId, value: Op) {
self.get_mut(id).unwrap().1 = value;
}
}
fn main() {
let mut statements = StatementsRecord::default();
let lhs_id = statements.insert("lhs".to_string(), Op::Num(64));
let rhs_id = statements.insert("rhs".to_string(), Op::Num(64));
// 64+64 = 128
let mut op_id = statements.insert("op".to_string(), Op::Add((lhs_id, rhs_id)));
// (64+64) + (64+64) = 256
let mut op_id = statements.insert("op".to_string(), Op::Add((op_id, op_id)));
let mut stack = Vec::new();
loop {
let result: Elem;
match statements.get(&op_id).unwrap() {
(op_name, Op::Num(_)) => {
panic!("{}: Did not expect Num", op_name);
},
(op_name, Op::Add((lhs, rhs))) => {
if let (_, Op::Num(lhs_value)) = statements.get(lhs).unwrap() {
if let (_, Op::Num(rhs_value)) = statements.get(rhs).unwrap() {
result = lhs_value + rhs_value;
} else {
stack.push(op_id);
op_id = *rhs;
continue;
}
} else {
stack.push(op_id);
op_id = *lhs;
continue;
}
},
};
if let Some(new_op_id) = stack.pop() {
statements.update(&op_id, Op::Num(result));
op_id = new_op_id;
} else {
println!("Result: {}", result);
return;
}
}
}
Prints
Result: 256

How do I tackle lifetimes in Rust?

I am having issues with the concept of lifetimes in rust. I am trying to use the crate bgpkit_parser to read in a bz2 file via url link and then create a radix trie.
One field extracted from the file is the AS Path which I have named path in my code within the build_routetable function. I am having trouble as to why rust does not like let origin = clean_path.last() which takes the last element in the vector.
fn as_parser(element: &BgpElem) -> Vec<u32> {
let x = &element.as_path.as_ref().unwrap().segments[0];
let mut as_vec = &Vec::new();
let mut as_path: Vec<u32> = Vec::new();
if let AsPathSegment::AsSequence(value) = x {
as_vec = value;
}
for i in as_vec {
as_path.push(i.asn);
}
return as_path;
}
fn prefix_parser(element: &BgpElem) -> String {
let subnet_id = element.prefix.prefix.ip().to_string().to_owned();
let prefix_id = element.prefix.prefix.prefix().to_string().to_owned();
let prefix = format!("{}/{}", subnet_id, prefix_id);//.as_str();
return prefix;
}
fn get_aspath(raw_aspath: Vec<u32>) -> Vec<u32> {
let mut as_path = Vec::new();
for i in raw_aspath {
if i < 64511 {
if as_path.contains(&i) {
continue;
}
else {
as_path.push(i);
}
}
else if 65535 < i && i < 4000000000 {
if as_path.contains(&i) {
continue;
}
else {
as_path.push(i);
}
}
}
return as_path;
}
fn build_routetable(mut trie4: Trie<String, Option<&u32>>, mut trie6: Trie<String, Option<&u32>>) {
let url: &str = "http://archive.routeviews.org/route-views.chile/\
bgpdata/2022.06/RIBS/rib.20220601.0000.bz2";
let parser = BgpkitParser::new(url).unwrap();
let mut count = 0;
for elem in parser {
if elem.elem_type == bgpkit_parser::ElemType::ANNOUNCE {
let record_timestamp = &elem.timestamp;
let record_type = "A";
let peer = &elem.peer_ip;
let prefix = prefix_parser(&elem);
let path = as_parser(&elem);
let clean_path = get_aspath(path);
// Issue is on the below line
// `clean_path` does not live long enough
// borrowed value does not live long
// enough rustc E0597
// main.rs(103, 9): `clean_path` dropped
// here while still borrowed
// main.rs(77, 91): let's call the
// lifetime of this reference `'1`
// main.rs(92, 17): argument requires
// that `clean_path` is borrowed for `'1`
let origin = clean_path.last(); //issue line
if prefix.contains(":") {
trie6.insert(prefix, origin);
}
else {
trie4.insert(prefix, origin);
}
count+=1;
if count >= 10000 {
println!("{:?} | {:?} | {:?} | {:?} | {:?}",
record_type, record_timestamp, peer, prefix, path);
count=0
}
};
}
println!("Trie4 size: {:?} prefixes", trie4.len());
println!("Trie6 size: {:?} prefixes", trie6.len());
}
Short answer: you're "inserting" a reference. But what's being referenced doesn't outlive what it's being inserted into.
Longer: The hint is your trie4 argument, the signature of which is this:
mut trie4: Trie<String, Option<&u32>>
So that lives beyond the length of the loop where things are declared. This is all in the loop:
let origin = clean_path.last(); //issue line
if prefix.contains(":") {
trie6.insert(prefix, origin);
}
While origin is a Vec<u32> and that's fine, the insert method is no doubt taking a String and either an Option<&u32> or a &u32. Obviously a key/value pair. But here's your problem: the value has to live as long as the collection, but your value is the last element contained in the Vec<u32>, which goes away! So you can't put something into it that will not live as long as the "container" object! Rust has just saved you from dangling references (just like it's supposed to).
Basically, your containers should be Trie<String, Option<u32>> without the reference, and then this'll all just work fine. Your problem is that the elements are references, and not just contained regular values, and given the size of what you're containing, it's actually smaller to contain a u32 than a reference (pointer size (though actually, it'll likely be the same either way, because alignment issues)).
Also of note: trie4 and trie6 will both be gone at the end of this function call, because they were moved into this function (not references or mutable references). I hope that's what you want.

Issues with Rust timelines and ownerships

I am trying to create a hashmap by reading a file. Below is the code that I have written. The twist is that I need to persist subset_description till the next iteration so that I can store it in the hasmap and then finally return the hashmap.
fn myfunction(filename: &Path) -> io::Result<HashMap<&str, &str>> {
let mut SIF = HashMap::new();
let file = File::open(filename).unwrap();
let mut subset_description = "";
for line in BufReader::new(file).lines() {
let thisline = line?;
let line_split: Vec<&str> = thisline.split("=").collect();
subset_description = if thisline.starts_with("a") {
let subset_description = line_split[1].trim();
subset_description
} else {
""
};
let subset_ids = if thisline.starts_with("b") {
let subset_ids = line_split[1].split(",");
let subset_ids = subset_ids.map(|s| s.trim());
subset_ids.collect()
} else {
Vec::new()
};
for k in subset_ids {
SIF.insert(k, subset_description);
println!("");
}
if thisline.starts_with("!dataset_table_begin") {
break;
}
}
Ok(SIF)
}
I am getting the below error and not able to resolve this
error[E0515]: cannot return value referencing local variable `thisline`
--> src/main.rs:73:5
|
51 | let line_split: Vec<&str> = thisline.split("=").collect();
| -------- `thisline` is borrowed here
...
73 | Ok(SIF)
| ^^^^^^^ returns a value referencing data owned by the current function
The problem lies within the guarantees the Rust makes on your behalf. The root of the problem can be seen as following. You are reading a file and manipulating it's content into a HashMap, and you are trying to return reference to the the data you read. But by returning a reference you would need to guarantee, that the strings in the file wont be changed later on, which you naturally can not do.
In Rust terms you keep trying to return references to local variables, which get dropped at the end of the function, which would efficiently leave you with dangling pointers. Here is the changes I made, even though they may not be most efficient, they do compile.
fn myfunction(filename: &Path) -> io::Result<HashMap<String, String>> {
let mut SIF = HashMap::new();
let file = File::open(filename).unwrap();
let mut subset_description = "";
for line in BufReader::new(file).lines() {
let thisline = line?;
let line_split: Vec<String> = thisline.split("=").map(|s| s.to_string()).collect();
subset_description = if thisline.starts_with("a") {
let subset_description = line_split[1].trim();
subset_description
} else {
""
};
let subset_ids = if thisline.starts_with("b") {
let subset_ids = line_split[1].split(",");
let subset_ids = subset_ids.map(|s| s.trim());
subset_ids.map(|s| s.to_string()).collect()
} else {
Vec::new()
};
for k in subset_ids {
SIF.insert(k, subset_description.to_string());
println!("");
}
if thisline.starts_with("!dataset_table_begin") {
break;
}
}
Ok(SIF)
}
As you can see, now you give away the ownership of strings in return value. This is achieved by modifying the return type and using to_string() function, to give away the ownership of local strings to HashMap.
There is an argument that to_string() is slow, so you can explore the use of into or to_owned(), but as I am not proficient with those constructs I can not assist you in optimization.

Is there a way to release a binding before it goes out of scope?

I'm trying to parse a file using regexes:
extern crate regex; // 1.0.1
use regex::Regex;
fn example(
section_header_pattern: Regex,
section_name: &str,
mut line: String,
mut is_in_right_section: bool,
) {
loop {
if let Some(m) = section_header_pattern
.captures(&line)
.and_then(|c| c.get(1))
{
is_in_right_section = m.as_str().eq(section_name);
line.clear();
continue;
}
}
}
fn main() {}
...but the compiler complains because the RegEx's captures() method has a borrow which endures for the lifetime of the match:
error[E0502]: cannot borrow `line` as mutable because it is also borrowed as immutable
--> src/main.rs:17:13
|
13 | .captures(&line)
| ---- immutable borrow occurs here
...
17 | line.clear();
| ^^^^ mutable borrow occurs here
18 | continue;
19 | }
| - immutable borrow ends here
By the time I get to line.clear();, I'm done with the Match and would like to clear the buffer and move onto the next line in the file without further processing. Is there a good/clean/elegant/idiomatic solution or do I need to just bite the bullet and introduce a subsequent 'if' block?
Short answer: No.
I'm done with the Match
You may be, but the compiler doesn't know that. Specifically, lifetimes are currently bound to the lexical scope they are defined in. The feature you are looking for is called non-lexical lifetimes. It's not stable now, but it's planned to be enabled in the Rust 2018 edition.
As an example:
fn main() {
let mut s = String::from("hello");
let matched = &s[..];
println!("{}", matched);
s.clear();
println!("{}", s);
}
A programmer can tell we are done with matched after we print it, but the compiler says that the borrow lasts until the closing }. The fix is to introduce a scope:
fn main() {
let mut s = String::from("hello");
{
let matched = &s[..];
println!("{}", matched);
}
s.clear();
println!("{}", s);
}
Your case is more insidious, as the decision to clear the string is interwoven with the value of the borrow of the string itself. Something like this would be my first place to reach:
fn main() {
let mut s = String::from("hello");
let do_clear;
{
let matched = &s[..];
println!("{}", matched);
do_clear = matched.contains("ll");
}
if do_clear {
s.clear();
}
println!("{}", s);
}
However, your specific case might be able to be transformed to avoid multiple if / if let statements:
let is_in_right_section = section_header_pattern.captures(&line)
.and_then(|c| c.get(1))
.map_or(false, |m| m.as_str() == section_name);
if is_in_right_section {
line.clear();
continue;
}
Which wouldn't look too bad if you introduce a new type and/or method. As a bonus, there's a place for the Regex to live:
struct Section(Regex);
impl Section {
fn is(&self, s: &str, section: &str) -> bool {
self.0
.captures(s)
.and_then(|c| c.get(1))
.map_or(false, |m| m.as_str() == section)
}
}
// ----
if section.is(&line, section_name) {
line.clear();
continue;
}
The original code works as-is when NLL is enabled:
#![feature(nll)]
extern crate regex; // 1.0.1
use regex::Regex;
fn main() {
let section_header_pattern = Regex::new(".").unwrap();
let section_name = "";
let mut line = String::new();
let mut is_in_right_section = false;
loop {
if let Some(m) = section_header_pattern
.captures(&line)
.and_then(|c| c.get(1))
{
is_in_right_section = m.as_str().eq(section_name);
line.clear();
continue;
}
return; // I don't really want to loop
}
}

Finding a way to solve "...does not live long enough"

I'm building a multiplex in rust. It's one of my first applications and a great learning experience!
However, I'm facing a problem and I cannot find out how to solve it in rust:
Whenever a new channel is added to the multiplex, I have to listen for data on this channel.
The new channel is allocated on the stack when it is requested by the open() function.
However, this channel must not be allocated on the stack but on the heap somehow, because it should stay alive and should not be freed in the next iteration of my receiving loop.
Right now my code looks like this (v0.10-pre):
extern crate collections;
extern crate sync;
use std::comm::{Chan, Port, Select};
use std::mem::size_of_val;
use std::io::ChanWriter;
use std::io::{ChanWriter, PortReader};
use collections::hashmap::HashMap;
use sync::{rendezvous, SyncPort, SyncChan};
use std::task::try;
use std::rc::Rc;
struct MultiplexStream {
internal_port: Port<(u32, Option<(Port<~[u8]>, Chan<~[u8]>)>)>,
internal_chan: Chan<u32>
}
impl MultiplexStream {
fn new(downstream: (Port<~[u8]>, Chan<~[u8]>)) -> ~MultiplexStream {
let (downstream_port, downstream_chan) = downstream;
let (p1, c1): (Port<u32>, Chan<u32>) = Chan::new();
let (p2, c2):
(Port<(u32, Option<(Port<~[u8]>, Chan<~[u8]>)>)>,
Chan<(u32, Option<(Port<~[u8]>, Chan<~[u8]>)>)>) = Chan::new();
let mux = ~MultiplexStream {
internal_port: p2,
internal_chan: c1
};
spawn(proc() {
let mut pool = Select::new();
let mut by_port_num = HashMap::new();
let mut by_handle_id = HashMap::new();
let mut handle_id2port_num = HashMap::new();
let mut internal_handle = pool.handle(&p1);
let mut downstream_handle = pool.handle(&downstream_port);
unsafe {
internal_handle.add();
downstream_handle.add();
}
loop {
let handle_id = pool.wait();
if handle_id == internal_handle.id() {
// setup new port
let port_num: u32 = p1.recv();
if by_port_num.contains_key(&port_num) {
c2.send((port_num, None))
}
else {
let (p1_,c1_): (Port<~[u8]>, Chan<~[u8]>) = Chan::new();
let (p2_,c2_): (Port<~[u8]>, Chan<~[u8]>) = Chan::new();
/********************************/
let mut h = pool.handle(&p1_); // <--
/********************************/
/* the error is HERE ^^^ */
/********************************/
unsafe { h.add() };
by_port_num.insert(port_num, c2_);
handle_id2port_num.insert(h.id(), port_num);
by_handle_id.insert(h.id(), h);
c2.send((port_num, Some((p2_,c1_))));
}
}
else if handle_id == downstream_handle.id() {
// demultiplex
let res = try(proc() {
let mut reader = PortReader::new(downstream_port);
let port_num = reader.read_le_u32().unwrap();
let data = reader.read_to_end().unwrap();
return (port_num, data);
});
if res.is_ok() {
let (port_num, data) = res.unwrap();
by_port_num.get(&port_num).send(data);
}
else {
// TODO: handle error
}
}
else {
// multiplex
let h = by_handle_id.get_mut(&handle_id);
let port_num = handle_id2port_num.get(&handle_id);
let port_num = *port_num;
let data = h.recv();
try(proc() {
let mut writer = ChanWriter::new(downstream_chan);
writer.write_le_u32(port_num);
writer.write(data);
writer.flush();
});
// todo check if chan was closed
}
}
});
return mux;
}
fn open(self, port_num: u32) -> Result<(Port<~[u8]>, Chan<~[u8]>), ()> {
let res = try(proc() {
self.internal_chan.send(port_num);
let (n, res) = self.internal_port.recv();
assert!(n == port_num);
return res;
});
if res.is_err() {
return Err(());
}
let res = res.unwrap();
if res.is_none() {
return Err(());
}
let (p,c) = res.unwrap();
return Ok((p,c));
}
}
And the compiler raises this error:
multiplex_stream.rs:81:31: 81:35 error: `p1_` does not live long enough
multiplex_stream.rs:81 let mut h = pool.handle(&p1_);
^~~~
multiplex_stream.rs:48:16: 122:4 note: reference must be valid for the block at 48:15...
multiplex_stream.rs:48 spawn(proc() {
multiplex_stream.rs:49 let mut pool = Select::new();
multiplex_stream.rs:50 let mut by_port_num = HashMap::new();
multiplex_stream.rs:51 let mut by_handle_id = HashMap::new();
multiplex_stream.rs:52 let mut handle_id2port_num = HashMap::new();
multiplex_stream.rs:53
...
multiplex_stream.rs:77:11: 87:7 note: ...but borrowed value is only valid for the block at 77:10
multiplex_stream.rs:77 else {
multiplex_stream.rs:78 let (p1_,c1_): (Port<~[u8]>, Chan<~[u8]>) = Chan::new();
multiplex_stream.rs:79 let (p2_,c2_): (Port<~[u8]>, Chan<~[u8]>) = Chan::new();
multiplex_stream.rs:80
multiplex_stream.rs:81 let mut h = pool.handle(&p1_);
multiplex_stream.rs:82 unsafe { h.add() };
Does anyone have an idea how to solve this issue?
The problem is that the new channel that you create does not live long enough—its scope is that of the else block only. You need to ensure that it will live longer—its scope must be at least that of pool.
I haven't made the effort to understand precisely what your code is doing, but what I would expect to be the simplest way to ensure the lifetime of the ports is long enough is to place it into a vector at the same scope as pool, e.g. let ports = ~[];, inserting it with ports.push(p1_); and then taking the reference as &ports[ports.len() - 1]. Sorry, that won't cut it—you can't add new items to a vector while references to its elements are active. You'll need to restructure things somewhat if you want that appraoch to work.

Resources