Borrowed values for and if blocks

I am trying to extend the crate rust-snmp. From what I can tell, it cannot walk mibs. It can perform a getnext and a getbulk but does not continue to do so until reaching the next column. Below is my code attempting to implement this.

fn get_bulk(addr: &'static str, non_repeat: u32, max_repetitions: u32, oids: Vec<&'static [u32]>) {
    /// The community string and timeout need to be moved to main
    ///
    /// non_repeat is the number of non-repeaters
    let community = b"maybe_public";
    let timeout = Duration::from_secs(10);

    /// 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() as usize;

    let handle = thread::spawn( move || {

        let mut mutable_oids = oids.clone();
        let mut sess = SyncSession::new(
            addr, community, Some(timeout), 0).unwrap();
        let response = sess.getbulk(
            mutable_oids.clone(),
            non_repeat, max_repetitions).unwrap();
        let mut col = 0_usize;
        for (name, val) in response.varbinds {
            if col == cols {
                col = 0;
                row += 1;
            }
            println!("{}. {} {} => {:?}", row, addr, name, val);
            /// Find the last table row and add those each column's
            /// objectidentifiers to the new oids
            if row == max_repetitions {
                let returned = name.to_string();
                let temp = &oids[col].to_vec();
                let oid: String = temp.iter().map(|x| x.to_string() + ".").collect();
                if returned.starts_with(&oid) {
                    let split = returned.split('.').collect::<Vec<&str>>();
                    let next_getbulk = split.into_iter()
                        .map(|c| c.parse::<u32>().unwrap())
                        .collect::<Vec<u32>>();
                    println!("{:?}", next_getbulk.as_slice());
                    mutable_oids[col] = next_getbulk.as_slice();
                }
            }
            col += 1;
        }
    });
    handle.join();
}

As you can easily imagine, I get borrowed value does not live long enough with next_getbulk .as_slice() as soon as I am out of the if block. I've tried every way I can think of to get around this borrow error and have given up enough and posted for help.

If you want to store dynamically generated vectors as static slices, you'll have to leak the memory or similar (llkely frowned upon in a library).

Are you going to ultimately return them or something? Once you enter the loop it looks like you just assign to mutable_oids and never read it. If you do need to keep it, could it be a Vec<Vec<u32>>?

Thanks for the response. It's sole purpose is to assign it to assign the slice into the column and that's it. I'll need a while loop that keeps calling response until something happens. Maybe a column's value does not "starts_with" the original oid, in which case it's a new SNMP table column. Maybe that column is then set to null. Anyway the idea is to keep looping over response adding the last returned oid columns in a row until empty.

I think I see. The original get_bulk took non-static references and a slice instead of a Vec. I'm guessing you got rid of those because of borrow problems with your thread. If you use crossbeam's scoped threads, things become much simpler.

Here I've done that, and replaced modified_oids with an owning version outside of your loop(s). I tried to comment the relevant changes. I'm not entirely sure I've gotten your logic correct, but hopefully it at least gives you some direction.

Thanks!! I'll give it a shot tomorrow.

Based on your response. I have a three questions. First, regards the change from Vec to outer slice. I could never get that to work. The oids look like:

   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 oids = &[ip_route_dest,
            ip_route_mask,
            ip_route_next_hop,
            ip_route_proto,
            ip_route_age];

I would always get an error because &[&[ue32]] size isn't known at compile time. It there a way around that?

The second question regards.

 // You're doing more allocation here than necessary but
 // I haven't bothered to clean it up
 let returned = name.to_string();
 let temp = &oids[col].to_vec();
 let oid: String = temp.iter().map(|x| x.to_string() + ".").collect();
 if returned.starts_with(&oid) {```

I wasn't sure how else to compare the original and returned oid as the rust-snmp crate takes in the oid as a slice but returns it the usual snmp form, 1.3.6.1.2.1.4.21.1.1.10.0.0.0 and I need to make sure at each returned oid starts with its original, else I know I'm done retrieving that snmp table's column. If there is a way without all that allocation let me know.

The final question regards those the line where we apply the returned oid to the current column..

collected_oids[col] =  next_getbulk.as_slice();

Thinking about it, I started to wonder about this. Since I will query multiple devices in parallel, shouldn't each have it's own mutable copy of the Vec of oids?

Bonus question. Why is the real programming problems always more difficult than the ones in a book?

That works fine. Are you sure you didn't have &[[u32]] somewhere instead? (Note how the version that works has a second & before the second [.)

My comment was mostly about the temporaries; you don't need most of them, e.g.:

    let oid: String = oids[col].iter().map(|x| x.to_string() + ".").collect();
    if name.starts_with(&oid) {
        let next_getbulk = name
            .split('.')
            .map(|c| c.parse::<u32>().unwrap())
            .collect::<Vec<u32>>();
        // ...
    }

You could make some function to simultaneously parse and store next_getbulk and check against oids[col] at the same time but I doubt it's worth it.

I don't know enough to answer this, really. Parallelization is certainly it's own bag of fun and complication.

It's when I pass them

get_bulk(agent_addrs, 0, 5,  oids)

fn get_bulk(agents: Vec<&str>, non_repeat: u32, max_repetitions: u32, oids: &[&[u32]]

I get ^^^^ expected &[&[u32]], found array [&[{integer}; 10]; 5]

Ah, I see. You have something like this somewhere:

let oids = [
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
];

Which is an "array of 5 (reference to an array of 10 u32s)s".

You can change it to:

let oids: &[&[u32]] = &[
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
];

(If it wasn't nested, you could call

get_bulk(agent_addrs, 0, 5, &oids[..]);

But because it's nested, you need to coerce the inside array references to slices as well somehow.)

Ah, coercion!! Works well :slight_smile:

Crossbeam is awesome.. if do this

crossbeam::scope(|scope| {
        for agent in agents {
            scope.spawn(move |_| {
               ......

Then I really start polling in parallel. This creates another question. When doing all this and getting the data in database, should I just take what is returned and put it directly into the database? Or should I store it in a structure first, then insert into the database :thinking: :thinking:

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.