Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Convention for constructing lifetime-bound values from raw pointers #556

Merged
merged 11 commits into from
Feb 4, 2015
136 changes: 136 additions & 0 deletions text/0000-raw-lifetime-anchors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
- Start Date: 2015-01-06
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary

Provide more flexible, less error-prone lifetime support
for unsafe pointer conversions throughout the libraries.

The currently tedious:
```rust
let s = std::slice::from_raw_buf(&ptr, len)
std::mem::copy_lifetime(self, s)
```
Becomes:
```rust
std::slice::from_raw_buf_with_lifetime(ptr, len, self)
```
Or, after a breaking change of the current API:
```rust
std::slice::from_raw_buf(ptr, len, self)
```

Static lifetime can be easy too:
```rust
std::slice::from_raw_buf(ptr, len, std::mem::STATIC)
```

# Motivation

The current library convention on functions constructing lifetime-bound
values from raw pointers has the pointer passed by reference, which
reference's lifetime is carried over to the return value.
Unfortunately, the lifetime of a raw pointer is often not indicative
of the lifetime of the pointed-to data. This may lead to mistakes
since the acquired reference crosses into the "safe" domain without
much indication in the code, while the pointed-to value may become
invalid at any time.

A typical use case where the lifetime needs to be adjusted is
in bindings to a foregn library, when returning a reference to an object's
inner value (we know from the library's API contract that
the inner data's lifetime is bound to the containing object):
```rust
impl Outer {
fn inner_str(&self) -> &[u8] {
unsafe {
let p = ffi::outer_get_inner_str(&self.raw);
let s = std::slice::from_raw_buf(p, libc::strlen(p));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the indentation on this line is a tiny bit off

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks.

std::mem::copy_lifetime(self, s)
}
}
}
```

And here's a plausible gotcha:
```rust
let foo = unsafe { ffi::new_foo() };
let s = unsafe { std::slice::from_raw_buf(&foo.data, foo.len) };
// s lives as long as foo

// some lines later

unsafe { ffi::free_foo(foo) };

// more lines later, in perfectly safe-looking code:

let guess_what = s[0];
```

# Detailed design

The signature of `from_raw*` constructors is changed: the raw pointer is
passed by value, and a generic reference argument is appended to work as a
lifetime anchor (the value can be anything and is ignored):

```rust
fn from_raw_buf_with_lifetime<'a, T, Sized? U>(ptr: *const T, len: uint,
life_anchor: &'a U)
-> &'a [T]
```
```rust
fn from_raw_mut_buf_with_lifetime<'a, T, Sized? U>(ptr: *mut T, len: uint,
life_anchor: &'a U)
-> &'a mut [T]
```

The existing constructors can be deprecated, to open a migration
path towards reusing their shorter names with the new signatures
when the time to break the API is right.

The current usage can be mechanically changed:
```rust
let s = std::slice::from_raw_buf(&ptr, len)
```
to:
```rust
std::slice::from_raw_buf_with_lifetime(ptr, len, &ptr)
```
However, it's better to try to find a more appropriate lifetime anchor
for each use.

## Fix copy_mut_lifetime

While we are at it, the first parameter of `std::mem::copy_mut_lifetime`
could be made a non-mutable reference. There is no reason for the lifetime
anchor to be mutable: the pointer's mutability is usually the relevant
question, and it's an unsafe function to begin with. This wart makes
code tedious, mut-happy, or transmute-happy in cases when a container
providing the lifetime for a mutable view into its contents is not itself
necessarily mutable.

## Anchor for static references

To facilitate conversion of pointers to static references, an anchor constant
is provided in `std::mem`:

```rust
pub const STATIC: &'static () = &();
```

# Drawbacks

The proposal adds new functions to the library for something that is
already doable, albeit with some inconvenience. Replacement of the existing
constructors, if approved, is a breaking change.

# Alternatives

Not adding the lifetime-anchored conversion functions, continuing to use the
current convention despite its problems and inconvenience.

# Unresolved questions

Should the existing by-reference constructors be deprecated and eventually
removed? If yes, should this change be done before 1.0?