Still not clear with "creates a temporary which is freed while still in use"

Let's assume we have simple recursive structure in Rust with explicitly defined lifetime:

struct SimpleNode<'a> {
  value: bool,
  next: Option<&'a SimpleNode<'a>>
}

Then we provide simple implementation:

impl SimpleNode<'_> {
  fn set<'a>(&mut self) {
    self.next = Some(&SimpleNode {
      value: false,
      next: None
    })
  }
}

So far such code working and compiling.
The next step we slightly improve our set function:

fn set<'a>(&mut self, val: bool) {
  self.next = Some(&SimpleNode {
    value: val,
    next: None
  })
}

now we having issue.

creates a temporary which is freed while still in use

Also if we change out set function implementation to this:

fn set<'a>(&mut self, val: bool) {
    self.value = val
}

The code will compile and work.

Primitive types implemented Copy trait. It has new lifetime every time we assign to new variable. Why and how it can be freed? I feel totally lost.

You are creating a temporary reference in the function &SimpleNode{...} which is freed once you leave the function. Instead, you should receive an external &SimpleNode and set it. This way the lifetime of val's reference will live at least as long as &self.

impl<'a, 'b: 'a> SimpleNode<'a> {
    fn set(&mut self, val: &'b SimpleNode) {
        self.next = Some(val);
    }
}

Note: If you're trying to create a linked list, I would suggest reading this: Introduction - Learning Rust With Entirely Too Many Linked Lists

1 Like

Ouch, what is that syntax? I don't recall coming across that anywhere.

How is it different from:

struct SimpleNode<'a> {
  value: bool,
  next: Option<&'a SimpleNode<'a>>
}

impl<'a> SimpleNode<'a> {
    fn set(&mut self, val: &'a SimpleNode) {
        self.next = Some(val);
    }
}

Lifetime Subtyping. Advanced Lifetimes - The Rust Programming Language

It's needed in order to express that something must outlive something else, but that they don't have the same lifetime.

Ah, got it. Thank you.

That is in chapter 19 Advanced Features. Along with "unsafe" and "macros" both areas where I have dared not to tread so far...

Then if this is the case why this code doesn't give any issue?

fn set<'a>(&mut self) {
    self.next = Some(&SimpleNode {
      value: true,
      next: None
    })
  }

We are also creating a temporary reference &SimpleNode{...} here, but code compile and run...

That is because the node is a compile time constant, so it's a reference into the compiled executable with a static lifetime. It's the same deal as string literals.

LukeTPeterson has me worried now.

The implications of that lifetime subtyping thing is that as a "not advanced" user I could write a ton of code with all kind of structs perhaps all kind of relations between them. Everything might compile just fine. Then when I come to use it things fail in incomprehensible ways because I have used the wrong life time ticks.

This is the situation described in chapter 19.

Then I have to become a "advanced user" and read chapter 19 to find out how to fix my code.

That rather means there is no escape, one has to read and understand everything in the book from the get go. You have to start out advanced!

The lifetime subtyping is not necessary. A &'b T will automatically convert to a &'a T if 'a is smaller than 'b. In fact, that's what happens inside the body of their set. It would be just as valid (and imo more idiomatic) to use

impl<'a> SimpleNode<'a> {
    fn set(&mut self, val: &'a SimpleNode) {
        self.next = Some(val);
    }
}

and let the shortening of the lifetime happen at the call site. I don't think it is necessary to know about except in very rare cases such as &mut T<'a> with a lifetime tick on the type behind the mutable reference (i.e. not the lifetime on the reference itself), as you can otherwise just shorten the lifetime.

Note that their example does not compile, because the extra lifetime <'b> has to go on the set function instead of the impl block.

1 Like

Now I'm confused. I compiled their example before posting my question:

struct SimpleNode<'a> {
  value: bool,
  next: Option<&'a SimpleNode<'a>>
}


impl<'a, 'b: 'a> SimpleNode<'a> {
    fn set(&mut self, val: &'b SimpleNode) {
        self.next = Some(val);
    }
}


fn main () {
   let s1 = SimpleNode {
       value: true,
       next: None
   };
   let mut s2 = SimpleNode {
       value: true,
       next: None
   };

   s2.set(&s1);
}

No error there.

1 Like

Huh, I guess I'm wrong about that part. I assumed it would give an analogous error to

error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
 --> src/main.rs:7:10
  |
7 | impl<'a, T: 'a> SimpleNode<'a> {
  |          ^ unconstrained type parameter

Regardless, it semantically belongs to set, not to the impl block.

I got it because defined lifetime 'a is derived from lifetime 'static so everything which belongs to lifetime 'static belongs to 'a as well, but not vice versa.
This is missing point.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.