【问题标题】:How do I return data from String.split().collect()?如何从 String.split().collect() 返回数据?
【发布时间】:2021-08-05 14:30:02
【问题描述】:

最近我一直在尝试开发游戏 Super Star Trek 的 Rust 版本。但是,我在加载已保存的游戏时遇到了一个小问题。

要加载游戏,我将保存文件读入字符串,将其拆分为 Vec (以 \0x1e 作为分隔符),然后在两个结果元素上使用 serde_json::from_str 以获取我的游戏数据对象(一个用于 Enterprise,另一个用于 Universe)。

不幸的是,当我尝试退货时,我收到以下错误:


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

据我所知,这是因为来自 .split.collect() 的数据被称为“临时”。

如何才能返回结构体?

编辑:这里是 github 仓库:https://github.com/thescribe11/Rust-SST

我的代码:

    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))
}

【问题讨论】:

  • 您的示例应用程序无法编译,但从错误消息来看,我猜您的entuni 变量包含对局部变量的引用。当函数返回时,该局部变量将不再存在,因此引用将指向已释放的内存。生锈可以防止这种非法行为。
  • 您的uni 似乎包含一些引用,可能是&amp;str,它指向raw_parts[1] 的一部分。 raw_partsget_config_from_file 末尾超出范围,所以uni 不能活得更长。但是,您要退回它,所以它比这更长寿。 Rust 禁止这样做。最简单的解决方案:将&amp;str 更改为字符串。
  • @sk_pleasant-EliasHolzmann 唉,那个解决方案不起作用。
  • 对我有用。我正在写一个包含更多细节的答案。
  • 您的 Universe&lt;'a&gt; 持有对其解析的数据的引用,根据生命周期注释的存在来判断。如果没有源字符串,您将无法拥有它。

标签: rust serde


【解决方案1】:

我将通过删除与问题无关的所有内容来大大简化您的代码:

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

这仍然会给出以下错误消息:

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

这里有什么问题?

uni.player_name 是一个包含&amp;strVec。这个&amp;str 借用(意思是:是对raw 的引用——但rawget_config_from_file 的末尾超出了范围,所以uni 不能活得更久。但是,您尝试返回 uni,这会使它的生存时间比 get_config_from_file 的结尾长——Rust 编译器会检测到这个问题并通过无法编译您的代码来阻止它。

解决此问题的最简单解决方案是不使用&amp;str,而是使用String。由于这意味着 'a 生命周期现在未使用,因此您还需要将其删除。此外,您需要删除 #[serde(borrow)] - 重点是代码不应再借用,因此该属性现在是错误的。

代码现在看起来像这样(并且可以编译):

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    • 1970-01-01
    • 2012-02-29
    • 2015-02-20
    • 2021-08-31
    • 2021-02-25
    • 1970-01-01
    相关资源
    最近更新 更多