How to make a struct which handle a self-referential type ? (it's more complicate than that))

I apologize for the title which is not very clear, but I don't know how to describe my problem.
I try to make a program that do some dimensional analysis.
What I do is that a make a tree for the dimensions (a unit like “kg”, a power like “m²”, or a calculation like “U × I”).
Then I want to have my dimension with only the ones from the SI.
For now, it was quite simple to implement this.

But now I want to stock the dimension and its SI representation in a same struct.

So first, we have an enum Dimension<'a> which is a tree that represent a calculation.

pub enum Dimension<'di> {
    Unite(String),
    Power(&'di RawDimension<'di>, i32),
    Composed(&'di Dimension<'di>, Op, &'di Dimension<'di>),
}

enum Op {
    Plus,
    Minus
}

Add, a method to get the SI representation

impl<'di> Dimension<'di> {
    fn into_SI(&'di self) -> Vec<Dimension> {
        // some code
       vec![]
    }
}

Finally, we have a struct Final<'a> which handle a Dimension<'a> add its result.

struct Final<'di> {
    dimension: Dimension<'di>,
    SI: Vec<Dimention<'di>>
}

BUT, when the time comes to initialize Final, everything goes wrong.

impl<'di> From<Dimension<'di>> for Final<'di> {
    fn from(dim: Dimension<'di>) -> Final<'di> {
        Final {
            dimension: dim,   // dim is owned by the dimention field
            SI: dim.into_SI(), // but it's borrow here at the same time !
        }
    }
}

Link to the playground

I try to use Pin, but I don't manage to make it work.
Do you have any solution ?

This looks like the sort of problem where one should not use references, but ownership. Most tree data structures should not use references (and this is like an expression tree).

Use Box<Dimension> instead of &'di Dimension<'di>. Once you do that, the struct Dimension will no longer need to have any lifetime parameter, because it owns its “child nodes” and so they always live long enough by definition.

If you expect to have repeated subtrees (e.g. if a unit is already SI so there is no different non-SI version) then use Rc<Dimension> instead of Box<Dimension>, to allow sharing one copy.

3 Likes

BTW, Rust's naming conventions use into for methods that destroy the original by taking fn into(self) (not borrow). You have .to_SI().

But it is a self-referential struct, and it can't work. Pin won't help.

Pin doesn't enable making self-referential structs. It's more like a type-system warning about self-referential structs that already exist. But user code can't make such structs, only the Rust compiler can, and only as a hidden implementation detail of async code.

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.