0

Our final freshman year coding project was creating a logic simulator (the basic NOT, AND, OR, etc. gates) in C++. I decided I would rewrite it in Rust for some hands-on experience.

Right now, I'm working on creating an in-memory representation of the circuit from the input. I have a Simulation struct which holds the wires and gates:

pub struct Simulation {
    wires: Vec<Wire>,
    gates: Vec<Gate>,
}

This is the centralized spot where I store these, however, I need to access the wires and gates in other parts of my code.

Currently, I'm using static references in the Wire and Gate structs (I'm not sure I need a static lifetime but that's another topic). However, I'm getting errors when I try to return references.

impl Simulation {
    pub fn get_wire(&self, num: usize) -> Result<&'static Wire, &str> {
        for wire in &self.wires {
            if wire.number == num {
                // Error: lifetime may not live long enough
                // returning this value requires that `'1` must outlive `'static`
                return Ok(wire);
            }
        }
        Err("No wire found")
    }
}

Since the Simulation struct will stick around for the whole program, there shouldn't be any issues with a reference to a wire or gate outliving the simulation. I've read the book about references, but there weren't any examples close this complex.

What am I missing? How can I use references in the rest of my program? Do I need to put it in an Rc<>?

I've tried a few different ways to access the wires, but each gives similar errors.

  • Maybe (just a wild guess), you need to add some lifetime info to Self, in this case, such that the 'static instance works. The way you write it, it looks leaky: Some instance with some lifetime as input and a 'static Wire as output looks inconsistent. – BitTickler Aug 13 '23 at 18:48
  • try: `impl <'t> Simulation { pub fn get_wire(&'t self, num: usize) -> Result<&'t Wire, &str> { for wire in &self.wires { if wire.number == num { // Error: lifetime may not live long enough // returning this value requires that `'1` must outlive `'static` return Ok(wire); } } Err("No wire found") } }` – BitTickler Aug 13 '23 at 18:56

1 Answers1

4

If you want to return a 'static reference, you need to make sure you take a 'static reference. Otherwise, you could end up with a dangling reference, when the lifetime specified is 'static but the data doesn't actually live for 'static.

So you need to take &'static self:

pub fn get_wire(&'static self, num: usize) -> Result<&'static Wire, &str> {
    for wire in &self.wires {
        if wire.number == num {
            return Ok(wire);
        }
    }
    Err("No wire found")
}

However, instead of constraining yourself to 'static for no reason, it is better to just be generic over the lifetime and return whatever lifetime that the parameter has:

pub fn get_wire<'a>(&'a self, num: usize) -> Result<&'a Wire, &str> {
    for wire in &self.wires {
        if wire.number == num {
            return Ok(wire);
        }
    }
    Err("No wire found")
}

Which, thanks to lifetime elision, can be shortened to:

pub fn get_wire(&self, num: usize) -> Result<&Wire, &str> {
    for wire in &self.wires {
        if wire.number == num {
            return Ok(wire);
        }
    }
    Err("No wire found")
}

And two last notes: first, there is a place where you actually should use 'static, but you didn't and therefore over-constrained the type: the returned &str error. It is actually &'static str (a string literal), but you specified it with no lifetime, so it has the same lifetime as &self (again, lifetime elision), which is constraining it more than needed and can cause problems for the caller needlessly.

And second, your whole function is basically Iterator::find():

pub fn get_wire(&self, num: usize) -> Result<&Wire, &'static str> {
    self.wires.iter().find(|wire| wire.number == num).ok_or("No wire found")
}

Learning to use iterators will help you with shorter and more idiomatic code.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • Wow, thank you so much! I'm coming from C++/Java, so I'm slowly learning how to use iterators, and they're really growing on me – Sam DeCook Aug 13 '23 at 19:26