-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
Hi,
if a struct is bound by a lifetime which is not used in the body of the struct, this bound seems to be excluded from typechecking.
Consider following functions (not valid Rust, because the function and struct bodies are omitted):
struct BananaTree;
struct Banana<'a>;
fn create_banana<'a>(tree: &'a BananaTree) -> Banana<'a>;
fn banana_calories<'a>(banana: Banana<'a>) -> int;
fn transform_banana<'a>(banana: Banana<'a>) -> Banana<'a>;
Banana
s are created from BananaTree
s and their lifetime must not exceed the lifetime of their tree. banana_calories
expects the passed banana to be alive, and transform_banana
creates a banana from another one, preserving the lifetime information.
The lifetimes should cause the following function to be rejected by the compiler, because the result of transform_banana
has lifetime bound to the lifetime of tree
, which is tied to the enclosing block. leaked_banana
and the following call to banana_calories
should then be invalid.
fn main() {
let leaked_banana = {
let tree = BananaTree { ... };
let banana = create_banana(&tree);
transform_banana(banana)
};
println!("{}", banana_calories(leaked_banana));
}
If the bananas and functions are defined like this:
struct BananaTree { calories: int, }
struct Banana<'a> { calories: &'a int }
fn create_banana<'a>(tree: &'a BananaTree) -> Banana<'a> {
Banana { calories: &tree.calories }
}
fn banana_calories<'a>(banana: Banana<'a>) -> int {
*banana.calories
}
fn transform_banana<'a>(banana: Banana<'a>) -> Banana<'a> {
banana
}
The lifetime bound 'a
in Banana<'a>
is used in the field calories
, forcing the borrow checks and making the invalid use shown above fail with appropriate error message (tree
does not live long enough):
fn main() {
let leaked_banana = {
let tree = BananaTree { calories: 105 };
let banana = create_banana(&tree);
transform_banana(banana)
};
println!("{}", banana_calories(leaked_banana));
}
On the other hand, if the implementation is like:
struct BananaTree;
struct Banana<'a> { calories: int }
fn create_banana<'a>(tree: &'a BananaTree) -> Banana<'a> {
Banana { calories: 105 }
}
fn banana_calories<'a>(banana: Banana<'a>) -> int {
banana.calories
}
fn transform_banana<'a>(banana: Banana<'a>) -> Banana<'a> {
banana
}
Then the lifetime bound on Banana<'a>
is not used in the member fields at all, but it still should be tied to the lifetime of the BananaTree
passed to create_banana
and it should limit the use of the created banana. It doesn't work like that, though, because the following function compiles:
fn main() {
let leaked_banana = {
let tree = BananaTree;
let banana = create_banana(&tree);
transform_banana(banana)
};
println!("{}", banana_calories(leaked_banana));
}
The lifetime of banana from create_banana
is clearly not tied to lifetime of tree
, because the compiler allows us to use the banana when its mother tree went out of scope.
This is in fact safe, because Banana
didn't contain any reference to the BananaTree
. The point is that I would like to use Rust's lifetimes in a C library bindings, where the Banana
values live as long as their respective BananaTree
s (as a matter of fact, the library is LLVM, banana trees are modules/functions and bananas are values).