Unable to specify lifetime parameter 解决编译错误

Unable to specify lifetime parameter to solve compilation error

我正在通过做一些小事来学习 Rust。我目前正在编写这个应用程序,所以它的第一步是读取 config.json 文件,但我遇到了无法解决的编译错误。

这是我的 Cargo.toml 依赖项

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
regex = "1.4.3"

这是代码

use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;

use regex::Regex;
use serde_json::Value;

fn _get_config() -> Box<dyn FnMut() -> &Value> {
    let mut config = Box::new(Value::Null);
    let a = || {
        if *config == Value::Null {
            match File::open("config.json").and_then(|file| -> Result<Value, std::io::Error> {
                serde_json::from_reader(BufReader::new(file)).map_err(|e| e.into())
            }) {
                Ok(v) => *config = v,
                Err(_) => {
                    *config = serde_json::from_str(
                        r#"
                            {
                                "DOMAIN_AS_ROOT_FOLDER": false,
                                "secret": "abcxyz"
                            }
                        "#,
                    )
                    .expect("Cannot initialize config, abort !");
                }
            }
        }
        config.as_ref()
    };
    Box::new(a)
}

fn main() {
    let get_config = _get_config();
    get_config();
}

这里是编译错误

❯ cargo run
   
error[E0106]: missing lifetime specifier
 --> src/main.rs:9:40
  |
9 | fn _get_config() -> Box<dyn FnMut() -> &Value> {
  |                                        ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
  = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'a` lifetime
  |
9 | fn _get_config() -> Box<dyn for<'a> FnMut() -> &'a Value> {
  |                             ^^^^^^^            ^^^
help: consider using the `'static` lifetime
  |
9 | fn _get_config() -> Box<dyn FnMut() -> &'static Value> {
  |                                        ^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0106`.
error: could not compile `sieve_generator`

To learn more, run the command again with --verbose.

基本上 _get_config() returns 一个闭包,让我每次调用它时都能得到 config 对象。我不明白为什么会出现这样的错误,因为变量 config 应该与我的闭包一样长,这里没有其他参数,为什么这里需要生命周期?我该如何解决?

非常感谢大家的宝贵时间。非常感谢。

你永远无法做你想做的事,因为你不能向编译器保证闭包永远不会超出应用程序生命周期的范围(在这种情况下您必须这样做,因为编译器不知道您将保留 &Value 引用多长时间。

相反,因为看起来您只从磁盘读取配置一次,为什么不借助 lazy_static 板条箱将其存储在静态变量中。

use std::fs::File;
use std::io::BufReader;
use serde_json::Value;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};

lazy_static! {
    static ref CONFIG: Arc<Mutex<Value>> = {
        let file: std::io::Result<Value> = File::open( "config.json" ).and_then( | f | { 
            serde_json::from_reader( BufReader::new( f ) )
                .map_err( | e | e.into( ) )
        } );
        Arc::new( Mutex::new( match file {
            Ok( v ) => v,
            _ => { 
                serde_json::from_str( 
                    r#"
                    {
                        "DOMAIN_AS_ROOT_FOLDER": false,
                        "secret": "abcxyz"
                    }
                    "#
                 )
                .expect( "Cannot initialize config, abort !" )
            }
        } ) )
    };
}

fn main( ) {
    let config = CONFIG.lock( ).unwrap( );
    // Use your config here.
}

编辑:

仅当您计划改变 Value 时才需要 ArcMutex。如果您不打算在某个时候改变配置,那么您可以放弃使用 ArcMutex.

在这两个示例中,我删除了 Option 类型,因为它不需要。

lazy_static! {
    static ref CONFIG: Value = {
        let file: std::io::Result<Value> = File::open( "config.json" ).and_then( | f | { 
            serde_json::from_reader( BufReader::new( f ) )
                .map_err( | e | e.into( ) )
        } );
        match file {
            Ok( v ) => v,
            _ => { 
                serde_json::from_str( 
                    r#"
                    {
                        "DOMAIN_AS_ROOT_FOLDER": false,
                        "secret": "abcxyz"
                    }
                    "#
                 )
                .expect( "Cannot initialize config, abort !" )
            }
        }
    };
}