放入struct时,值的寿命不足 [英] Value doesn't live long enough when put in struct

查看:71
本文介绍了放入struct时,值的寿命不足的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用此板条箱在Rust中使用LLVM.我试图创建一个代码生成器结构来保存我的上下文,模块和生成器,但是当我尝试编译时,出现一条错误消息,提示c does not live long enough.我怎样才能编译它?为什么c的生存时间不够长?

I'm trying to work with LLVM in Rust using this crate. I'm trying to create a code generator struct to hold the context, module, and builder for me, but when I try to compile I get an error message that says c does not live long enough. How can I get this to compile, and why isn't c living long enough?

代码:

use llvm::*;
use llvm::Attribute::*;
pub struct CodeGen<'l> {
    context: CBox<Context>,
    builder: CSemiBox<'l, Builder>,
    module: CSemiBox<'l, Module>,
}
impl<'l> CodeGen<'l> {
    pub fn new() -> CodeGen<'l> {
        let c = Context::new();
        let b = Builder::new(&c);
        let m = Module::new("test", &c);
        CodeGen {
            context: c,
            builder: b,
            module: m,
        }
    }
}

完整错误消息:

error: `c` does not live long enough
  --> src/codegen.rs:17:31
   |
17 |         let b = Builder::new(&c);
   |                               ^ does not live long enough
...
24 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'l as defined on the body at 15:32...
  --> src/codegen.rs:15:33
   |
15 |     pub fn new() -> CodeGen<'l> {
   |                                 ^

error: `c` does not live long enough
  --> src/codegen.rs:18:38
   |
18 |         let m = Module::new("test", &c);
   |                                      ^ does not live long enough
...
24 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'l as defined on the body at 15:32...
  --> src/codegen.rs:15:33
   |
15 |     pub fn new() -> CodeGen<'l> {
   |                                 ^

error: aborting due to 2 previous errors

推荐答案

这看起来像是一生的遗忘使事情变得不那么清晰的情况之一.

This looks like one of those situations where lifetime elision makes things less clear.

这是原型Builder::new:

pub fn new(context: &Context) -> CSemiBox<Builder>

这可能会让您认为CSemiBoxcontext的生存期没有任何关系.但是CSemiBox定义具有生命周期参数:

Which might make you think that the CSemiBox doesn't have any relation to the lifetime of context. But the definition of CSemiBox has a lifetime parameter:

pub struct CSemiBox<'a, D>

据我了解,当函数的输出类型(在本例中为Builder::new)具有有效期参数时,如果只有一个输入有效期,则可以将其删除. (生存期删除规则在这本书此问题.)在这种情况下,输出生存期与输入生存期相同.这意味着之前的原型实际上等效于以下内容:

As I understand it, when the output type of a function (in this case Builder::new) has a lifetime parameter, it can be elided if there is only one input lifetime. (The lifetime elision rules are described in the book and in this question.) In this case, the output lifetime is taken to be the same as the input lifetime. That means the prototype from before is actually equivalent to the following:

pub fn new<'a>(context: &'a Context) -> CSemiBox<'a, Builder>

我希望这可以澄清正在发生的事情:在Builder::new(&c)之后,CSemiBox包含对其创建的Context的引用(b包含对c的引用).您不能将bc放在相同的结构中,因为编译器必须能够证明c的寿命超过b.有关更全面的说明,请参见

I hope this clarifies what's happening: after Builder::new(&c), the CSemiBox contains a reference to the Context it was created from (b contains a reference to c). You can't put b and c in the same struct because the compiler has to be able to prove that c outlives b. For a more thorough explanation, see Why can't I store a value and a reference to that value in the same struct?

我可以想到两种方法来处理此问题. (您无法使用Rc,因为您无法控制板条箱.)

There are two ways I can think of to handle this. (You can't use Rc because you don't control the crate.)

  1. 不要将Context存储在CodeGen结构中.您在组织代码的方式上受到限制,但这并不一定很糟糕.

  1. Don't store the Context inside the CodeGen struct. You're limited in how you can structure your code, but that's not necessarily bad.

由于Context存储在堆中,因此可以使用unsafe使引用(显示为)具有'static生存期.类似于以下代码片段的代码应该可以正常工作,该代码将从CodeGen中删除生命周期注释.如果您这样做(随时使用unsafe),则您有责任确保公开界面的安全性.例如,这意味着CodeGen无法分发对buildermodule的引用,因为这可能会泄漏对context'static引用.

Since the Context is stored on the heap, you can use unsafe to make the references (appear to) have a 'static lifetime. Something like the following snippet ought to work, which removes the lifetime annotation from CodeGen. If you do this (as any time you use unsafe), you take responsibility for ensuring the safety of the exposed interface. That means, for example, CodeGen can't hand out references to builder and module, because that could leak a 'static reference to context.

pub struct CodeGen {
    context: CBox<Context>,
    builder: CSemiBox<'static, Builder>,
    module: CSemiBox<'static, Module>,
}
impl CodeGen {
    pub fn new() -> CodeGen {
        let c = Context::new();  // returns a CBox<Context>
        let c_static_ref: &'static _ = unsafe {
            let c_ptr = c.as_ptr() as *const _;  // get the underlying heap pointer
            &*c_ptr
        };
        let b = Builder::new(c_static_ref);
        let m = Module::new("test", c_static_ref);
        CodeGen {
            context: c,
            builder: b,
            module: m,
        }
    }
}

这篇关于放入struct时,值的寿命不足的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆