How to create struct with references to it's own items


#1

I’m trying to create a struct the has references to it’s own items. So far I’ve come up with this:

pub struct Smash<'h> {
	hero : usize,
	hero_desc : &'h HeroDesc,
	shield_skill : Option<&'h Skill>,
}

With a new function like this:

impl<'h> Smash<'h> {
	pub fn new(hero : usize, hero_desc : &'h HeroDesc) -> Self {
		Smash {
			hero,
			hero_desc: hero_desc,
			shield_skill: hero_desc.get_skill("EXPLOSIVESHIELD"),
		}
	}
}

Where get_skill has this signature pub fn get_skill<'a>(&'a self, name : &str) -> Option<&'a Skill>

That compiles, but I’m not positive it’s correct. My more immediate problem is that I don’t know how to call this new function correctly. I have an owned object of HeroDesc and am attempting to clone it:

let hero_descs = vec![ HeroDesc::ironman(), HeroDesc::hulk() ];
...
Smash::new(1usize, hero_descs[1].clone() )

But this gives me a mismatched type error.

What I’m really looking to do is move the ownership of the HeroDesc into the Smash struct, and then create references to items inside of it in the same struct.


#2

Using

	let hero_descs = vec![ HeroDesc::ironman(), HeroDesc::hulk() ];
	Smash::new(1usize, &hero_descs[1] );

compiles just fine.

I want you to note that this struct is not referencing its own items, since hero_desc gets its reference from outside and shield_skill gets it from hero_desc. Moreover, referencing your own items generally causes problems. But if you insist in cloning you may do the following.

struct Smash<'h> {
	hero : usize,
	hero_desc : HeroDesc,
	shield_skill : Option<&'h Skill>,
}

impl<'h> Smash<'h> {
	fn new(hero : usize, hero_desc : HeroDesc) -> Self {
		let shield_skill=hero_desc.get_skill("EXPLOSIVESHIELD");
		Smash {
			hero,
			hero_desc,
			shield_skill,
		}
	}
}

fn main()
{
	let hero_descs = vec![ HeroDesc::ironman(), HeroDesc::hulk() ];
	Smash::new(1usize, hero_descs[1].clone() );
}

#3

Note I’m doing a clone() here:

Smash::new(1usize, &hero_descs[1].clone() );

I need a copy of the hero_desc at that point: it should not refer to the same item.

I’ll see if I can change the struct to the other format you gave (I was trying that approach originally, just couldn’t figure out how to get shield_skill referenced properly).

I could probably live without the clone in this scenario, but I’d like to know what I’m doing wrong nonetheless


#4

I tried your second code and am getting the error "hero_desc does not live long enough". Am I missing something subtle?

pub struct Smash<'h> {
	hero : usize,
	hero_desc : HeroDesc,
	shield_skill : Option<&'h Skill>,
}

impl<'h> Smash<'h> {
	pub fn new(hero : usize, hero_desc : HeroDesc) -> Self {
		let shield_skill = hero_desc.get_skill("EXPLOSIVESHIELD");
		Smash {
			hero,
			hero_desc,
			shield_skill,
		}
	}
}

#5

The following code compiles.

#[derive(Clone)]
struct HeroDesc
{
}

struct Skill {}

impl HeroDesc
{
	fn ironman() -> Self
	{
		HeroDesc{}
	}
	fn hulk() -> Self
	{
		HeroDesc{}
	}
	fn get_skill<'a>(&self, name:&str)->Option<&'a Skill>
	{
		None
	}
}

struct Smash<'h> {
	hero : usize,
	hero_desc : HeroDesc,
	shield_skill : Option<&'h Skill>,
}

impl<'h> Smash<'h> {
	fn new(hero : usize, hero_desc : HeroDesc) -> Self {
		let shield_skill=hero_desc.get_skill("EXPLOSIVESHIELD");
		Smash {
			hero,
			hero_desc,
			shield_skill,
		}
	}
}

fn main()
{
	let hero_descs = vec![ HeroDesc::ironman(), HeroDesc::hulk() ];
	Smash::new(1usize, hero_descs[1].clone() );
}

So the error must be caused for something else. But as I said before, referencing your own fields generally causes problems; a little googling send me to this post Struct containing reference to own field .


#6

This is a bit suble: pass HeroDesc as a reference so that it is not destroyed inside the “new”.

Pointers in structs are absolute, and struct hierarchies live directly in stack frames. Any struct with pointers to itself can’t be moved, because that would invalidate all the pointers to itself… If you need proper pointers, use values in heap, allocated with Box or Rc.

Rust is a bit lower level than people initially assume :confused:


#7

In short, you can’t do that in Rust. There are some crates that facilitate this, such as rental, but it’s not otherwise something supported.


#8

Note that this method will only be implementable if 'a is 'static.


#9

Rust is a bit lower level than people initially assume

Yes, I’m starting to see that now. I’m trying to avoid just using Box and Rc. I’m guessing those aren’t the “Rust way” in most cases, and I’m trying ot force myself to be able to think like Rust as much as possible. Rc would imply variable sharing, which I’m not trying to do here… at least logically, technically I am sharing via an alias.


#10

In short, you can’t do that in Rust. There are some crates that facilitate this, such as rental, but it’s not otherwise something supported.

These leaves open many questions for me about how to achieve certain data structures and algorithms. I’ll to formulate my mental block and post a new question about this.


#11

Not just you, It is a toppik that is getting a lot of thout at the moment. Try googling self referencing struct and immovable generators