Why does rustc think value is still borrowed?

Here's my erroneous code:

use std::collections::HashSet;

pub struct Program<'a: 'b, 'b> {
    pub list: Vec<Box<dyn Op<'a, 'b>>>,
}

/* Here's a very simple example of a struct that could be in my Program.list.
I am keeping it commented because the error still happens without it.
*/
/*
#[derive(Debug)]
pub struct Instruction<'a>(Reg<'a>);
impl<'a: 'b, 'b> TaintAnalysis<'a, 'b> for Instruction<'a> {
    fn taint_analysis(&'b self, tainted: &mut HashSet<GlobalOrReg<'a, 'b>>) -> bool {
        todo!()
    }
}
impl<'a: 'b, 'b> Op<'a, 'b> for Instruction<'a> {}
*/

pub trait Op<'a: 'b, 'b>: std::fmt::Debug + TaintAnalysis<'a, 'b> {}
pub trait TaintAnalysis<'a: 'b, 'b> {
    fn taint_analysis(&'b self, tainted: &mut HashSet<GlobalOrReg<'a, 'b>>) -> bool;
}

pub enum GlobalOrReg<'a: 'b, 'b> {
    Global(&'b Global<'a>),
    Reg(&'b Reg<'a>),
    Other,
}

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Global<'a>(pub &'a str);

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Reg<'a>(pub &'a str);

fn main() -> Result<(), String> {
    let prog = Program {list: vec![/*Instruction(Reg("t1"))*/]};
    { // I added this scope to try to fix the error but it did nothing
        let mut tainted_set = HashSet::new();
        for op in &prog.list {
            if op.taint_analysis(&mut tainted_set) {
                println!("FLOW");
                return Ok(());
            }
        }
        println!("NO FLOW");
        return Ok(());
    }
}

The error:

   Compiling playground v0.0.1 (/playground)
error[E0597]: `prog.list` does not live long enough
  --> src/main.rs:42:19
   |
39 |     let prog = Program {list: vec![/*Instruction(Reg("t1"))*/]};
   |         ---- binding `prog` declared here
...
42 |         for op in &prog.list {
   |                   ^^^^^^^^^^ borrowed value does not live long enough
...
51 | }
   | -
   | |
   | `prog.list` dropped here while still borrowed
   | borrow might be used here, when `prog` is dropped and runs the destructor for type `Program<'_, '_>`

For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` (bin "playground") due to 1 previous error

I notice that when I remove tainted_set from main, the error goes away. So I think that means that Rust thinks that the references stored in tainted_set could still be around when prog is dropped. But that doesn't make sense to me, because tainted_set is dropped before prog, especially since I put it in its own scope. What is going on? What does the type analysis see here?

There's a few things going on...

  • Trait parameters are invariant
  • Trait parameters are part of the type of dyn Trait<..>
  • Trait objects have non-trivial destructors

When combined with this signature:

pub trait TaintAnalysis<'a: 'b, 'b> {
    fn taint_analysis(&'b self, tainted: &mut HashSet<GlobalOrReg<'a, 'b>>) -> bool;
}

When you create a &'b dyn Op<'a, 'b>, the trait object is borrowed forever,[1] and that conflicts with having a non-trivial destructor.


  1. the shared variant â†Šī¸Ž

1 Like

This compiles by letting the shared reference be shorter than the trait parameter, or you could move that to the method I suppose.

//                                          vvvvvvv                   vv
pub trait Op<'a: 'b, 'b>: std::fmt::Debug + for<'c> TaintAnalysis<'a, 'c> {}

(or)

    fn taint_analysis<'c>(&'c self, tainted: &mut HashSet<GlobalOrReg<'a, 'c>>) -> bool
    where
        'b: 'c;

I'm not sure if this gets rid of all the problems or fits your use case entirely.

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.