The idea behind a recursive struct

Greetings everyone, my first post on the forum (was reluctant to posting this on SO). I have very recently started working with Rust and have never delved into this programming paradigm before (which is honestly fascinating). The drawback I'm experiencing right now is having to rethink everything about design and behaviour to what I'm more familiar with.

I've been wanting to port yattag over to rust as I need to generate an enormously large number of HTML files as fast as possible; however before doing that I want to properly learn the language and the paradigm itself.

For instance--I would require a Tag struct which is able to maintain a property of itself like so:

struct Tag {
    tag: Tag,
    doc: &str,
    name: &str,
    attrs: &Vec<String>;
}

Which does not compile due to lifetime specifier being required. Thus I decide to convert this slightly to:

struct Tag<'a>
...
tag: &'a Tag,

..however the compiler states that error is due to the wrong number of lifetime parameters: expected 1, found 0.

I presume I'm just not structuring this right at all. The idea behind this is to be able to get both the current and parent tag.

1 Like

The inner Tag needs to know what its inner 'a is. You can write tag: &'a Tag<'a>.

The thing is, though, that this is an impossible structure. You can't require each Tag to contain a reference to another Tag. Where does it begin?

1 Like

Also, this is a struct, not a trait like the title says, yeah?

struct Tag<'a> {
    tag: Option<Box<Tag<'a>>>,
    doc: &'a str,
    name: &'a str,
    attrs: &'a Vec<String>,
}

Will compile. You need to Box up the inner Tag so that Rust can figure out the size, but since that would require making a Tag to instantiate a Tag, you can use Option so that you can initialize a tag with None.

I would imagine that you'd actually want this, though:

struct Tag {
    tag: Option<Box<Tag>>,
    doc: String,
    name: String,
    attrs: Vec<String>,
}

You almost always want to start with structs that own their contents, rather than just hold references. Specifically, &Vec seems unusual. But it does depend on what you're doing.

2 Likes

I thought so, thanks a bunch for clarifying. I'll probably implement it like so:

struct Tag {
   doc: &str,
    name: &str,
    attr: &Vec<String>,
}

trait Init {
  // Init methods
}

trait Enter {
 // Enter methods
}

trait Exit { 
 // Exit methods
}

...

//Implement stuff

The concept of classes and objects are quite different it seems with Rust.

Woops! Thanks for pointing that out. Giving it another thought, structs owning their own content does indeed make more sense. Thanks for your code suggestion :smile:

Unless you're planning on re-using those methods on other structs, you can just

impl Tag {
    // methods
}

To give it methods.

The concept of classes and objects are quite different it seems with Rust.

This is true :slight_smile:

Oh wow, that's much simpler indeed. Only Init would be used by more than one struct; rest is specific to each struct so just implementing them is much better. Thanks again :sweat_smile:

Any time. That's what we're here for!

I have seen that also enums are used for this type of task? why not use one in this case?

Also I will like to ask about c - Is it possible to have a linked list of different data types? - Stack Overflow (linked list of different data types)

I have seen that also enums are used for this type of task? why not use one in this case?

Option is an enum and it has a lot of convenience methods (e.g. take(), map(...), etc.).

Also I will like to ask about c - Is it possible to have a linked list of different data types? - Stack Overflow

Please open a new thread.