0

Suppose you have a struct with some owned data, and you want to write a constructor which takes ownership of some data, and also provides an alternative view into it. You might want to do this with a graph, say (have the graph represented by a linked list, but also provide a view into the nodes and edges of the graph via references).

With the following, contrived example, I define a struct that owns a String, and has references to the first half of the string, along with the second half. I then try to define a constructor which takes ownership of a String and defines two references to its contents.

pub struct StringAndParts<'a> {
    pub s: String,
    pub first_half: &'a str,
    pub second_half: &'a str,
}

impl<'a> StringAndParts<'a> {
    pub fn from_string(s: String) -> Result<StringAndParts<'a>, &'static str> {
        if s.chars().count() < 3 {
            return Err("StringAndParts::from_string - supplied string must take a string of length at least 2");
        }

        let halfway_point = s.len() / 2;

        let first_half = &s.as_str()[..halfway_point];
        let second_half = &s.as_str()[halfway_point..];
        Ok(StringAndParts {
            s,
            first_half,
            second_half,
        })
    }
}

fn main() {
    let s = String::from("Hello, world!");
    let maybe_sap = StringAndParts::from_string(s.clone());
    if let Ok(sap) = maybe_sap {
        format!(
            "The StringAndParts formed from {} looks like this: {}\n{:?}\n{:?}\n",
            s, sap.s, sap.first_half, sap.second_half
        );
    }
}

However, with cargo build, I get the following errors:

error[E0515]: cannot return value referencing function parameter `s`
  --> src/main.rs:17:9
   |
15 |           let first_half = &s.as_str()[..halfway_point];
   |                             ---------- `s` is borrowed here
16 |           let second_half = &s.as_str()[halfway_point..];
17 | /         Ok(StringAndParts {
18 | |             s,
19 | |             first_half,
20 | |             second_half,
21 | |         })
   | |__________^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing function parameter `s`
  --> src/main.rs:17:9
   |
16 |           let second_half = &s.as_str()[halfway_point..];
   |                              ---------- `s` is borrowed here
17 | /         Ok(StringAndParts {
18 | |             s,
19 | |             first_half,
20 | |             second_half,
21 | |         })
   | |__________^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `s` because it is borrowed
  --> src/main.rs:18:13
   |
7  |   impl<'a> StringAndParts<'a> {
   |        -- lifetime `'a` defined here
...
15 |           let first_half = &s.as_str()[..halfway_point];
   |                             ---------- borrow of `s` occurs here
16 |           let second_half = &s.as_str()[halfway_point..];
17 | /         Ok(StringAndParts {
18 | |             s,
   | |             ^ move out of `s` occurs here
19 | |             first_half,
20 | |             second_half,
21 | |         })
   | |__________- returning this value requires that `s` is borrowed for `'a`

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.

My understanding of this error is that the borrow checker isn't happy because s goes out of scope at the end of the function block, and first_half and second_half reference this data. However, as s is also returned as part of the function, I feel that s should still be considered "live". I'm slightly perplexed by the last error, returning this value requires that `s` is borrowed for `'a` --- any further lifetime annotations on the above code don't seem to make sense to me.

Is my understanding of the above errors correct? If so, how can I convince the Rust compiler that the data in first_half and second_half is valid as they are inextricably linked to s? I've tried shoehorning some more lifetimes on the definitions of first_half and second_half, but this doesn't appear to help the situation. Many thanks in advance.

steeps
  • 55
  • 6

0 Answers0