dimanche 29 janvier 2023

Lifetime of return value not directly related to parameter lifetime

I am new to rust, and having trouble figuring out the correct design pattern for what I am trying to do. In my program, there are Position structs, which reference a single coordinate system that they "belong" to. I have understood that rust must therefore be able to guarantee that coordinate_system reference outlives the Position. So far so good.

The issue is in a function such as transform_to_parent, where I want to create a new Position which is dependent on the lifetime of the exact coordinate_system that it later references, and not on the lifetime of the self parameter through which it accesses the coordinate system. This seems to be the only thing that lifetime specifiers would allow.

If I add a lifetime specifier to the following code, it compiles, but understandably complains when I let the old Position that I transformed from goes out of scope.

pub struct Position<'a> {
    // Position data omitted
    coordinate_system: &'a CoordinateSystem<'a>,
}

impl<'a> Position<'a> {
    fn transform_to_parent<'b>(self: &'b Self) -> Option<Position<'b>> {
        Some(Position {
            coordinate_system: self.coordinate_system.origin?.coordinate_system
        })
    }
}

pub struct CoordinateSystem<'a> {
    origin: Option<&'a Position<'a>>,
} 

// Test case
fn main() {
    // child_system is the child coordinate system of root_system, and has its origin at child_origin
    let root_system = CoordinateSystem { origin: None };
    let child_origin = Position { coordinate_system: &root_system };
    let child_system = CoordinateSystem { origin: Some(&child_origin) };

    let mut p2 = Position { coordinate_system: &child_system };
    {
        let p1 = Position { coordinate_system: &child_system };
        if let Some(x) = p1.transform_to_parent() { // Error: "borrowed value does not live long enough"
            p2 = x;
        }
    }
    // No-op, just pretend to use p2 again, after p1 has gone out of scope
    p2;
}

Is it possible for Rust to bind the lifetime of the function result to the lifetime of self.coordinate_system.get_origin()?.coordinate_system (i.e. the parent coordinate system), instead of the self? Is there a correct design pattern for something like this?

I assume that a ref-counting system would work, but I think that would be bad design, because there is clear ownership of the coordinate systems, and because the lifetime information should be deducible somehow.

Aucun commentaire:

Enregistrer un commentaire