Problems with field ref in struct

struct AA<'t> {
    kk: Vec<&'t str>
}
fn new<'t>(dd: String) -> AA<'t> {
    //dd is a very large string, and dd can't be changed to &'str.
    //want to return the struct AA
    //If kk is changed to Vec<String>, memory may be doubled.
    //So I want Vec<&'t str> to be a reference to the dd string, how do I do that?
    todo!()
}

You (normally) can't create self-referential structs in Rust. A solution is to keep a String outside the struct:

struct AA<'t> {
    kk: Vec<&'t str>
}

fn new<'t>(dd: &'t String) -> AA<'t> {
    AA {
        kk: vec![dd],
    }
}

fn main() {
    let s = String::from("Hello");
    let _aa = new(&s);
}

(Playground)

Edit: Actually you could/should write &'t str for the type of dd, but I just noticed that's exactly what you wanted to avoid.

The other thing you can do is this:

struct AA {
    dd: String,
    kk: Vec<std::ops::Range<usize>>,
}

Then, if you want the ith substring, you'd have:

impl AA {
    fn ith_substr(&self, i: usize) -> &str {
        &self.dd[self.kk[i]]
    }
}
3 Likes

You likely misunderstand how this works. If you move the string dd to the field kk, nothing is copied. At least not the large backing buffer of the string. Only the (pointer, capacity, length) triple is moved around, which is very cheap.

Note that it's not possible to "return by reference". That simply doesn't make any sense as a concept. References are non-owning views into data. If you want to return data created in a function, you'll have to do that by returning a value.

And that's not "suboptimal" or "slow". You have to have an owner of the data somewhere.

4 Likes
 let dd = "dddddd";
    let mut bbb = vec![];
    for _ in 1..1000000 {
        bbb.push(dd [0..2].to_owned());
    }
    println!("{:?}", bbb);
dd is copy...

the dd var form ToDD,so

trait ToDD {
    fn create_dd(&self) -> String;
}
fn new<'t>(dd_from: impl ToDD) -> AA<'t> {
    let dd=dd_from. create_dd();
}

To simplify the problem, I've simplified the code this way.

I need to move dd into to function.

Check out @DanielKeep's answer above.

It's a workable solution, but it's too complicated. thk.

thk

Such a treatment cannot be used with the following code

struct NN {}
struct AA<'t> {
    kk: Vec<&NN>,
}
fn new<'t>(dd: Vec<NN>) -> AA<'t> {
    todo!()
}

In what way? It's as simple as it gets. You store the ranges, you query the string when you need slices. The whole answer contains 9 lines of code. If that's too complicated for you, we likely won't be able to help.

In this case you can store dd along with a Vec<usize> containing indexes into kk rather than references.

That or look into some self-referential (that's the name of what you likely want) crate, like ouroboros.

For future reference, whenever you're trying to write a function that requires something with a lifetime (e.g. AA<'t> here) but there's nothing with a lifetime in input to the function then you're 99% trying to do something impossible, and you should stop and reason about who owns what. In your example here dd is a Vec owned by the function which will be destroyed when the function ends, so there's no way AA will be able to borrow from it.

11 Likes

This. Thank you for saying that. There was a very similar question yesterday with something like

impl<'a> Struct<'a> {
    fn new() -> Struct<'a> {
      // self-referential crimes
    }
}

and I knew from the signature it's going to be troublesome, but I was too lazy to phrase it adequately while on my phone.

3 Likes

One of the nice things about Rust is you can't accidentally copy a String's content. If you're trying to avoid that copy, you'll know you're on the right track if you never use .clone(), .to_owned(), .into(), etc. So,

struct AA {
    kk: Vec<String>,
}

fn new(dd: String) -> AA {
    AA { kk: vec![dd] }
}

Doesn't copy.

1 Like

I had exactly the same thought a couple of days ago when reasoning about the combination of the NewType pattern for ID types, such as:

struct MyLittleId(Uuid);

struct MyStruct {
  id: MyLittleId,
  ...
}

In GC languages, it's easier that a developer assigns the supposedly-unique id field when touching the codebase, while in Rust you would need to explicitly derive or implement the Clone and/or Copy traits on the MyLittleId type in order for you to be able to make such mistake. This alone increases the chances of such mistake getting caught either by the developer or during code review.

1 Like