如何在主函数中创建一个变量并调用需要具有“静态生命周期”的函数?

23

我定义了一个结构体,其中包含一个定义静态生命周期的函数:

impl MyStruct {
    pub fn doSomething(&'static self) {
        // Some code goes here
    }
}

我在主函数中这样使用:

fn main() {
    let obj = MyStruct {};
    obj.doSomething();
}

这里的意图是让doSomething调用阻塞并在应用程序的生命周期内执行。

我遇到了有关生命周期检查的问题,它表明它可能会超出main函数的生命周期,这对我来说似乎很奇怪,因为一旦main完成,应用程序就应该退出。

有没有办法实现这个?


1
你想要做什么? - Boiethios
1
@Boiethios,我说的就是这个问题。那个函数调用了另一个需要静态生命周期的函数,因此该函数本身需要静态生命周期才能符合这些要求。我需要从主函数中调用该函数,但编译器阻止了这样做,因为结构体显然比主函数存在更久的时间。 - user1282993
@Boiethios hyper在运行服务器和处理每个请求时需要静态运行时。更糟糕的是,它实际上是两层静态要求(两个闭包)。 - user1282993
@Boiethios,问题解决了!你能把它提交为答案,这样我就可以接受它了吗? - user1282993
@lyptt 抱歉,我很忙。你可以接受一个适合你的答案。 - Boiethios
显示剩余4条评论
2个回答

21
创建具有 "static" 生命周期的引用的主要方法是使变量成为静态变量。 静态变量是可以在编译时创建的变量:
struct MyStruct;

impl MyStruct {
    pub fn do_something(&'static self) {}
}

static OBJ: MyStruct = MyStruct;

fn main() {
    OBJ.do_something();
}

随着Rust的常量求值故事的改善,这种情况会更加普遍,但它永远不会允许在运行时进行配置。

一种更少见的方法是有意地泄漏内存,产生一个将“永久”持续的引用。这应该被反对,因为泄漏内存不是件好事:

fn main() {
    let obj = Box::leak(Box::new(MyStruct));
    obj.do_something();
}

还有创建单例的可能性:

因为一旦main完成,应用程序就会退出。

也许是这样,但编译器并不会特别处理main的生命周期。


当运行服务器和处理每个请求时,hyper需要一个静态运行时。

不,它没有。它有一个绑定为: 'static,这意味着传递的任何引用都必须是'static,但你根本不需要传递裸引用。

对于这种模式,最常见的做法是传递像Arc这样的东西。这允许共享底层资源。

pub fn do_something<F, T>(f: F)
where
    F: Fn() -> T + 'static,
    T: 'static,
{
    // "spawn" 3 threads
    f();
    f();
    f();
}

struct MyStruct;

static OBJ: MyStruct = MyStruct;

fn main() {
    // OK
    do_something(|| &OBJ);

    // Not OK
    let another = MyStruct;
    do_something(|| &another);

    // OK
    use std::sync::Arc;
    let shared = Arc::new(MyStruct);
    do_something(move || shared.clone());
}

你甚至可以使用内部可变性来进行动态重构。参见:内部可变性
另请参见:

是的,我有一个包装类需要一个对self的引用,我可以传递一个 Arc 来解决这个特定的问题,但它仍然有各种 'static 的问题。 - user1282993
根据Rust参考资料中的@Shepmaster所说,“静态物品在程序结束时不会调用drop”,因此,静态变量和Box :: leak基本上是相同的,除了后者在堆上分配内存。 说实话,考虑到Rust如何烦人地初始化静态变量,Box :: leak似乎是一种更好的使用运行时初始化创建静态变量的方式。 - Eloff
@Eloff 我不明白你评论的目的是什么。答案已经列出了泄漏内存作为一种解决方案。 - Shepmaster
1
@Shepmaster,你提到了“这应该被反对,因为泄漏内存不是一件好事”,确实听起来很可怕。但这正是Rust静态工作的方式,所以我认为在这种特定的用例中不应该被反对。 - Eloff

13

一种朴素的方法是使用 static 变量,但是如果您需要在 main 函数中实际设置值,则需要使用不安全的代码:

static mut OBJ: MyStruct = MyStruct;

fn main() {
    unsafe {
        OBJ = MyStruct {};
        OBJ.doSomething();
    }
}

对于可变静态内容,做任何操作都是不安全的

更好的方法是使用库(lazy_static)来处理不安全代码

use lazy_static::lazy_static;

fn main() {
    lazy_static!{
        static ref OBJ: MyStruct = MyStruct {};
    }
    OBJ.doSomething();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接