How do I return data from String.split().collect()?

Viewed 211

Recently I've been trying to develop a Rust version of the game Super Star Trek. However, I've ran into a slight issue in loading saved games.

To load a game, I'm reading the save file into a string, splitting it into Vec<&str> at (with \0x1e as the seperator), and then using serde_json::from_str on the two resulting elements to get my game data objects (one for the Enterprise, another for the universe).

Unfortunately, when I try to return them I get the following error:


    57 |     return Some((ent, uni))
    |               ^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

From what I can tell, this is because the data from .split.collect() is something called a "Temporary."

How do I make it possible to return the structs?

EDIT: Here's the github repo: https://github.com/thescribe11/Rust-SST

My code:

    use crate::structs::{Enterprise, Universe};

    use std::fs::{File};
    use std::io::{Read, Write, stdin, stdout};

    use serde_json::{to_string, from_str};

    <snip>

    pub fn get_config_from_file<'a> () -> Option<(Enterprise, Universe<'a>)>{
    //! Thaw a game.
    //!
    //! The .sst file type is as follows:
    //! <json data for Enterprise object>\0x1e<json data for Universe object>

    let mut save_file: File;

    loop {
        let temp = File::open(input("Save file: "));
        match temp {
            Ok(p) => {save_file = p; break;},
            Err(e) => {println!("Unable to find save file.\n"); continue;}
        };
    }

    let pass = input("Password: ");
    let mut enc_data = String::new();
    match save_file.read_to_string(&mut enc_data) {
        Ok(_) => {},
        Err(_) => {println!("\nERROR: The save file is corrupted."); return None}
    }

    let mut ent: Enterprise; let mut uni: Universe;
    let raw_parts: Vec<&str> = enc_data.split("\0x1e").collect();
    ent = match serde_json::from_str(raw_parts[0]) {
        Ok(data) => {data},
        Err(_) => {println!("\nERROR: The save file is corrupted."); return None}
    };
    uni = match serde_json::from_str(raw_parts[1]) {
        Ok(data) => data,
        Err(_) => {println!("\nERROR: The save file is corrupted."); return None}
    };

    return Some((ent, uni))
}
1 Answers

I'm going to heavily simplify your code by removing everything that is not pertinent to the problem:

use serde::{Deserialize};

#[derive(Debug, Deserialize)]
pub struct Universe<'a> {
    #[serde(borrow)]
    player_name: Vec<&'a str>,
}

pub fn get_config_from_file<'a> () -> Option<Universe<'a>> {
    let raw: String = "some_data".to_string();
    let uni: Universe = serde_json::from_str(&raw).unwrap();
    return Some(uni);
}

Playground

This still gives the following error message:

error[E0515]: cannot return value referencing local variable `raw`
  --> src/lib.rs:12:12
   |
11 |     let uni: Universe = serde_json::from_str(&raw).unwrap();
   |                                              ---- `raw` is borrowed here
12 |     return Some(uni);
   |            ^^^^^^^^^ returns a value referencing data owned by the current function

What is the problem here?

uni.player_name is a Vec containing a &str. This &str borrows (meaning: is a reference to) raw – but raw goes out of scope at the end of get_config_from_file, so uni can't live any longer than that. However, you try to return uni, which would make it live longer than the end of get_config_from_file – the Rust compiler detects this problem and prevents it by failing to compile your code.

The easiest solution to fix this problem is to not use a &str, but a String. As this means the 'a lifetime is now unused, you need to remove it as well. Also, you need to remove #[serde(borrow)] – the whole point in this is that the code should not borrow anymore, so that attribute is now wrong.

The code now looks like this (and does compile):

use serde::{Deserialize};

#[derive(Debug, Deserialize)]
pub struct Universe {
    player_name: Vec<String>,
}

pub fn get_config_from_file () -> Option<Universe> {
    let raw: String = "some_data".to_string();
    let uni: Universe = serde_json::from_str(&raw).unwrap();
    return Some(uni);
}

Playground

Related