我尝试使用this crate在Rust中使用LLVM。我试图创建一个代码生成器结构来为我保存上下文,模块和构建器,但是当我尝试编译时,我收到一条显示c does not live 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
答案 0 :(得分:2)
这看起来就像生活中的缺陷使事情变得不那么明显的情况之一。
这里是Builder::new
的{{3}}:
pub fn new(context: &Context) -> CSemiBox<Builder>
这可能会让您认为CSemiBox
与context
的生命周期没有任何关系。但CSemiBox
的{{3}}有一个生命周期参数:
pub struct CSemiBox<'a, D>
据我了解,当函数的输出类型(在这种情况下为Builder::new
)具有生命周期参数时,如果只有一个输入生命周期,则可以省略它。 (生命周期省略规则在prototype和definition中描述。)在这种情况下,输出生命周期与输入生命周期相同。这意味着之前的原型实际上等同于以下内容:
pub fn new<'a>(context: &'a Context) -> CSemiBox<'a, Builder>
我希望这可以澄清发生了什么:在Builder::new(&c)
之后,CSemiBox
包含对其创建的Context
的引用(b
包含引用到c
)。您不能将b
和c
放在同一个结构中,因为编译器必须能够证明c
超过b
。有关更详尽的说明,请参阅the book
我可以通过两种方式来处理这个问题。 (您无法使用Rc
,因为您无法控制箱子。)
不要将Context
存储在CodeGen
结构中。您在如何构建代码方面受到限制,但这并不一定是坏事。
由于Context
存储在堆上,您可以使用unsafe
使引用(看起来)具有'static
生命周期。类似下面的代码段应该有效,它会从CodeGen
中删除生命周期注释。如果您这样做(任何时候使用unsafe
),您负责确保公开界面的安全性。这意味着,例如,CodeGen
无法分发对builder
和module
的引用,因为这可能会泄漏对'static
的{{1}}引用。
context