< 返回版块

pader 发表于 2020-08-13 11:28

首先有个 AppConfig

#[derive(Deserialize, Clone, Debug)]
pub struct AppConfig {
    pub listen: String
}

然后通过这个函数读取配置:

use std::fs::read_to_string;
use serde::Deserialize;

/// 读取配置文件
///
/// 读取来自 res/config/app.toml 的配置文件
pub fn get_app_config() -> AppConfig {
    let read = read_to_string("res/config/app.toml");
    match read {
        Ok(text) => {
            let toml: Result<AppConfig, toml::de::Error> = toml::from_str(text.as_str());
            match toml {
                Ok(v) => v,
                Err(e) => {
                    println!("配置解析失败: {}", e.to_string());
                    exit(1);
                }
            }
        },
        Err(e) => {
            println!("读取配置文件失败: {}", e.to_string());
            exit(1);
        }
    }
}

以上代码运行一切正常。

我想写一个通用一点的函数,传入 struct 和文件名就可以读取到配置即可,于是写了这个

pub fn read_config<'a, T>(filename: &str) -> Result<T, String>
where
    T: Deserialize<'a>,
{
    let tr = read_to_string(format!("res/config/{}", filename));
    match tr {
        Ok(text) => {
            println!("{}", text);
            let tt: Result<T, toml::de::Error> = toml::from_str(text.as_str());
            match tt {
                Ok(t) => Ok(t),
                Err(e) => Err(e.to_string())
            }
        },
        Err(e) => Err(e.to_string())
    }
}

然后我把 get_app_config 替换成了

pub fn get_app_config() -> AppConfig {
    read_config::<AppConfig>("app.toml").unwrap()
}

结果就这样了

error[E0597]: `text` does not live long enough
  --> src\base\app.rs:47:65
   |
39 | pub fn read_config<'a, T>(filename: &str) -> Result<T, String>
   |                    -- lifetime `'a` defined here
...
47 |             let tt: Result<T, toml::de::Error> = toml::from_str(text.as_str());
   |                                                  ---------------^^^^----------
   |                                                  |              |
   |                                                  |              borrowed value does not live long enough
   |                                                  argument requires that `text` is borrowed for `'a`
...
52 |         },
   |          - `text` dropped here while still borrowed

这是什么鬼设定???具体哪里有区别吗??

评论区

写评论
TinusgragLin 2023-12-16 03:14

lz可以了解一下零拷贝这个概念,核心想法就是,比如这串json字符串已经读到了内存中的一个地方:

{"hello": "word"}

现在我要拿到hello字段的值,通常的做法就是复制一份"word"到内存的另一个地方,但如果你只是使用这个值而不会去修改它的话,更快捷的方法就是只引用(!)原本json字符串中的这一块,这样就省去了内存拷贝。

ribs 2023-10-26 11:32

我是这样理解的,关键在于返回值T是T: Deserialize<'a>含声明周期的对象。 再看toml::from_str的签名:

pub fn from_str<T>(s: &str) -> Result<T, Error>

补充上生命周期应该是:

pub fn from_str<'a, T>(s: &'a str) -> Result<T<'a>, Error>

这就要求参数s的生命周期不能比T短。 text在read_config方法结束后就销毁了,所以就报错了。

作者 pader 2023-10-25 23:18

恩,你的意思我明白,但是这个 text 传递给 toml 解析后理应已经变为另一个对象,已经不对原 text 持有任何所谓的引用了,这就好像比传递一个字符串给某个函数解析成 JSON 对象,结果这个 JSON 对象居然还引用着原来的那个字符串,理论上 JSON 对象应该跟原来的字符串没有任何关系了。

--
👇
rock117: 函数内部不能自己生成引用类型的数据并返回,这样函数体执行完,引用指向的内存就被释放了,这就是悬垂指针了,类比下c语言。返回的引用必须是直接或者间接基于函数参数产生的。

rock117 2020-09-19 00:17

函数内部不能自己生成引用类型的数据并返回,这样函数体执行完,引用指向的内存就被释放了,这就是悬垂指针了,类比下c语言。返回的引用必须是直接或者间接基于函数参数产生的。

作者 pader 2020-08-13 16:36

谢谢,被生命周期这个折腾的实在不轻,虽然可以运行了,但是看了这个 DeserializeOwned 的源码,还是很蒙圈。 还是得多看看。 。

--
👇
laizy: 因为你尝试将文件读出来的局部变量的数据借给T返回,自然要报错了,签名改成下面的:

pub fn read_config<T>(filename: &str) -> Result<T, String>
where
    T: DeserializeOwned,

--
👇
pader: 重点是写法相同,前面的就能正常运行,后面的无非就是换了个泛型,加了个生命周期?我也没看到具体哪里产生了影响。。。

laizy 2020-08-13 13:27

因为你尝试将文件读出来的局部变量的数据借给T返回,自然要报错了,签名改成下面的:

pub fn read_config<T>(filename: &str) -> Result<T, String>
where
    T: DeserializeOwned,

--
👇
pader: 重点是写法相同,前面的就能正常运行,后面的无非就是换了个泛型,加了个生命周期?我也没看到具体哪里产生了影响。。。

作者 pader 2020-08-13 12:40

重点是写法相同,前面的就能正常运行,后面的无非就是换了个泛型,加了个生命周期?我也没看到具体哪里产生了影响。。。

作者 pader 2020-08-13 12:39

试过了,还是一样。。

--
👇
Neutron3529: 试试let xxxxxx=test.as_str()然后把xxxxxx送进函数

具体原理不清楚,但这样的确有效

或许是生命周期的问题

Neutron3529 2020-08-13 12:37

试试let xxxxxx=test.as_str()然后把xxxxxx送进函数

具体原理不清楚,但这样的确有效

或许是生命周期的问题

1 共 9 条评论, 1 页