Lifetime '1 with mpsc::channel

I am trying to get data out of a scoped thread in crossbeam but I get this error

  let s = sender.clone();
   |                 - lifetime `'1` appears in the type of `s`
...
55 |                     let response = sess.getbulk(
   |                                    ^^^^ `sess` was mutably borrowed here in the previous iteration of the loop
...
81 |                             s.send((agent, n)).unwrap();
   |                             ------------------ argument requires that `sess` is borrowed for `'1`

I have never seen lifetime '1.

The code is below:

n get_bulk( agents: &[&str], community: &[u8], timeout: Duration, non_repeat: u32, max_repetitions: u32, oids: &[&[u32]]) {
    /// The community string and timeout need to be moved to main
    ///
    /// non_repeat is the number of non-repeaters
    /// Think in terms of row from table and each oid a column in the table
    ///  Which they actually are in
    let mut row = 1u32;
    let cols = oids.len();
    let (sender, receiver) = mpsc::channel();
    /// Use crossbeam crate.  Place each device for query
    /// into Crossbeam scope for parallel processing.
    crossbeam::scope(|scope| {

        for agent in agents {
            let s = sender.clone();
            scope.spawn(move |_| {
                let mut collected_oids: Vec<Vec<u32>> = oids.iter().map(|&s| s.to_owned()).collect();
                let mut sess = SyncSession::new(
                    agent, community, Some(timeout), 0).unwrap();
                let mut end_of_column = 0;
                while end_of_column < cols {
                    let mut oids_for_getbulk: Vec<&[u32]> = collected_oids.iter().map(|s| &s[..]).collect();
                    let response = sess.getbulk(
                        &oids_for_getbulk,
                        non_repeat, max_repetitions).unwrap();

                    let mut col = 0_usize;
                    for (name, val) in response.varbinds {
                        let n = name.clone();
                        //let v: = val.clone();
                        if col == cols {
                            col = 0;
                            row += 1;
                        }

                        /// Find the last table row and add those each column's
                        /// objectidentifiers to the new oids

                        let oid: String = oids[col].iter().map(|x| x.to_string() + ".").collect();
                        /// TODO needs to be logged, not printed
                        /// Keep track of fails. Remember, if snmp querying two
                        /// adjacent columns, at some point the next column
                        /// will be queried but should fail as you've already
                        /// queried it.
                        if !name.to_string().starts_with(&oid) {
                            println!("Failed: {} => {}", oid, name);
                        }
                        if name.to_string().starts_with(&oid) {
                            s.send((agent, n)).unwrap();

                            println!("{}. {} {} => {:?}", row, agent, name, val);
                            let next_getbulk = name.to_string()
                                .split('.')
                                .map(|c| c.parse::<u32>().unwrap())
                                .collect::<Vec<u32>>();
                            collected_oids[col] = next_getbulk;
                            if col == cols - 1 {
                                row += 1;
                            }
                        } else {

                            end_of_column += 1;
                            println!("End of column: {} => cols: {}", end_of_column, cols);
                            if !end_of_column < cols {
                                break;
                            }
                        }
                        col += 1;
                    }
                }
            });
        }
    }).unwrap();
    for (a, o) in receiver.iter() {
        println!("Received: {}: {}", a, o);
    }
}

Such lifetime names are sometimes appearing in diagnostics for unnamed lifetimes. Note that they are not valid rust syntax, so it's not surprising that you've never seen them before. I don't know it this is always the case but sometimes the error messages explain somewhere which lifetime it's calling “'1”. Either way, it might help if you provided a more complete error message.

You are correct, I missed a line or two in my copy and paste of the error.
I have also included the complete code. This will run just find if I do not send anything from the rust-snmp SyncSession struct. That is I do not get any error if I send the agent variable back, or just an i32. But I do get the error when trying to send those items that are produced by rust-snmp SyncSession, which are 'Value', and 'ObjectIdentifier'. Otherwise, it doesn't appear that I am borrowing SyncSession twice. I wonder if I need to make SyncSession and all of its fields cloneable?

message.error[E0499]: cannot borrow `sess` as mutable more than once at a time
  --> src/main.rs:58:36
   |
50 |             let s = sender.clone();
   |                 - lifetime `'1` appears in the type of `s`
...
58 |                     let response = sess.getbulk(
   |                                    ^^^^ `sess` was mutably borrowed here in the previous iteration of the loop
...
84 |                             s.send(SnmpResult(agent.to_string(), Arc::new(name))).unwrap();
   |                             ----------------------------------------------------- argument requires that `sess` is borrowed for `'1`

error: aborting due to previous error; 6 warnings emitted

For more information about this error, try `rustc --explain E0499`.
error: could not compile `use-rust-snmp`

To learn more, run the command again with --verbose.
use std::time::Duration;
use snmp::{SyncSession, ObjectIdentifier};
use crossbeam;
use std::sync::{mpsc, Arc};
use std::sync::mpsc::Sender;

#[derive(Debug)]
struct SnmpResult<'a>(String, Arc<ObjectIdentifier<'a>>);

fn main() {
    let ip_route_dest = &[1, 3, 6, 1, 2, 1, 4, 21, 1, 1, ];
    let ip_route_mask = &[1, 3, 6, 1, 2, 1, 4, 21, 1, 11, ];
    let ip_route_next_hop = &[1, 3, 6, 1, 2, 1, 4, 21, 1, 7, ];
    let ip_route_proto = &[1, 3, 6, 1, 2, 1, 4, 21, 1, 9, ];
    let ip_route_age = &[1, 3, 6, 1, 2, 1, 4, 21, 1, 10, ];

    let agent_addrs:&[&str] = &["10.0.251.15:161", "10.0.251.16:161"];

    let oids: &[&[u32]] = &[ip_route_dest,
        ip_route_mask,
        ip_route_next_hop,
        ip_route_proto,
        ip_route_age];

    let community:&[u8] = b"maybe-public";
    let timeout = Duration::from_secs(7);
    let (sender, receiver) :
        (Sender<SnmpResult>, _ ) = mpsc::channel();

    get_bulk( sender, agent_addrs, community, timeout, 0, 10,  oids);
    for (a) in receiver.iter() {
        println!("Received: {:?}", a.0);
    }
}

fn get_bulk( sender : mpsc::Sender<SnmpResult>, agents: &[&str], community: &[u8], timeout: Duration, non_repeat: u32, max_repetitions: u32, oids: &[&[u32]]) {
    /// The community string and timeout need to be moved to main
    ///
    /// non_repeat is the number of non-repeaters
    /// Think in terms of row from table and each oid a column in the table
    ///  Which they actually are in
    let mut row = 1u32;
    let cols = oids.len();
    //let (sender, receiver) = mpsc::channel();
    /// Use crossbeam crate.  Place each device for query
    /// into Crossbeam scope for parallel processing.
    crossbeam::scope(|scope| {

        for agent in agents {
            let s = sender.clone();
            scope.spawn(move |_| {
                let mut collected_oids: Vec<Vec<u32>> = oids.iter().map(|&s| s.to_owned()).collect();
                let mut sess = SyncSession::new(
                    agent, community, Some(timeout), 0).unwrap();
                let mut end_of_column = 0;
                while end_of_column < cols {
                    let mut oids_for_getbulk: Vec<&[u32]> = collected_oids.iter().map(|s| &s[..]).collect();
                    let response = sess.getbulk(
                        &oids_for_getbulk,
                        non_repeat, max_repetitions).unwrap();

                    let mut col = 0_usize;
                    for (name, val) in response.varbinds {
                        //let n = name.clone();
                        //let v: = val.clone();
                        if col == cols {
                            col = 0;
                            row += 1;
                        }

                        /// Find the last table row and add those each column's
                        /// objectidentifiers to the new oids

                        let oid: String = oids[col].iter().map(|x| x.to_string() + ".").collect();
                        /// TODO needs to be logged, not printed
                        /// Keep track of fails. Remember, if snmp querying two
                        /// adjacent columns, at some point the next column
                        /// will be queried but should fail as you've already
                        /// queried it.
                        if !name.to_string().starts_with(&oid) {
                            println!("Failed: {} => {}", oid, name);
                        }
                        if name.to_string().starts_with(&oid) {
                            s.send(SnmpResult(agent.to_string(), Arc::new(name))).unwrap();

                            println!("{}. {} {} => {:?}", row, agent, name, val);
                            let next_getbulk = name.to_string()
                                .split('.')
                                .map(|c| c.parse::<u32>().unwrap())
                                .collect::<Vec<u32>>();
                            collected_oids[col] = next_getbulk;
                            if col == cols - 1 {
                                row += 1;
                            }
                        } else {

                            end_of_column += 1;
                            println!("End of column: {} => cols: {}", end_of_column, cols);
                            if !end_of_column < cols {
                                break;
                            }
                        }
                        col += 1;
                    }
                }
            });
        }
    }).unwrap();

}

Cargo.toml

[dependencies]
crossbeam = "0.8.1"
crossbeam-channel = "0.5.1"

[dependencies.snmp]
path = "../rust-snmp"

Fixed it myself! :slight_smile: I implemented Display for the Value enum in rust-snmp crate which allowed me to get a value from the response as owned(?) and into the send side of the channel

if name.to_string().starts_with(&oid) {
                                //println!("{}. {} {} => {}", row, agent, name, val.to_string());
                                tx.send((row, agent.to_string(), name.to_string(), val.to_string())).unwrap();

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.