4

I'm trying to write a Rust struct. The struct owns a Reference counted pointer to a string and also owns a vector of string slices to the same string.

Furthermore I'm trying to write a function to generate this struct. I'm unsure how to proceed.

struct MyStruct<'a> {
    rc_string: Rc<String>,
    vec: Vec<&'a str>
}

fn build_my_struct<'a>(s: &Rc<String>) -> MyStruct<'a> {
    let rc_string = s.clone();
    let mut vec = Vec::new();
    vec.push(&rc_string[0..2]);

    MyStruct {
        rc_string: rc_string,
        vec: vec
    }
}
error[E0515]: cannot return value referencing local variable `rc_string`
  --> src/main.rs:13:5
   |
11 |       vec.push(&rc_string[0..2]);
   |                 --------- `rc_string` is borrowed here
12 | 
13 | /     MyStruct {
14 | |         rc_string: rc_string,
15 | |         vec: vec
16 | |     }
   | |_____^ returns a value referencing data owned by the current function

I understand that the vec variable has borrowed the rc_string. The compiler doesn't like returning vec because it has the borrow to the local variable rc_string.

However rc_string is being returned as well? The string slices are valid for the duration of the life of MyStruct.rc_string?

James Welchman
  • 105
  • 1
  • 6

1 Answers1

2

You need to borrow Rc for life time 'a as well. Compiler needs to know that slice from a String is living in 'a or not. In this case we need to borrow Rc for 'a and compiler will know inner of Rc will also live in 'a.

If you clone s and assign it to rc_string:

  • s will stay in the function's scope as borrowed Rc for lifetime 'a
  • rc_string will be the owner of the Rc pointer

and compiler won't be able to know slice of a rc_string is living for 'a or not.

Using slice from a s will work :

fn build_my_struct<'a>(s: &'a Rc<String>) -> MyStruct<'a> {
    let mut vec = Vec::new();
    let rc_string = s.clone();

    vec.push(&s[0..2]);

    MyStruct { rc_string, vec }
}

Playground

Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
  • @JamesWelchman The code in your second question will work with the same way, please check [this code](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e2d60a493392091514ee0016a198f56c). You can't return slice from a moved content unless it lives in static lifetime. – Ömer Erden Jul 04 '19 at 18:38
  • In my original code I use the `Rc` pointer *because* the lifetime of `MyStruct` extends beyond the lifetime of the`Rc` in the calling function. Just to be clear - I can't move *both* a String and a slice of the string into a return value? – James Welchman Jul 04 '19 at 18:50
  • Basically [this](https://play.rust-lang.org/?version=beta&mode=debug&edition=2015&gist=959c1f59a29376cca0311094f5e6cdd2) is always impossible. – James Welchman Jul 04 '19 at 19:00
  • @JamesWelchman borrow checker's duty is preventing you from facing with dangling pointers, you can create a struct both String and it's slice but there is a possibility at runtime you can move(or even drop) String value from a field of struct without moving or dropping slices from that string. With this you can have dangling pointers – Ömer Erden Jul 04 '19 at 19:08
  • 1
    @JamesWelchman [this](https://play.rust-lang.org/?version=beta&mode=debug&edition=2015&gist=959c1f59a29376cca0311094f5e6cdd2) is not possible because according to our definition `&s` needs to live in `my_struct`'s lifetime argument. It is a bit confusing because you are not dropping the real string but borrow checker is not able to know the string is trapped inside a `Rc` pointer. – Ömer Erden Jul 04 '19 at 19:15