book/ffi: nullable pointer, libc cleanups

Expand the "nullable pointer optimization" section with a code example.

Change examples to use std::os::raw instead of libc, when applicable.
This commit is contained in:
Alex Burka 2016-06-13 13:22:38 -04:00 committed by Alex Burka
parent a373b8437b
commit 032ea41e99

View File

@ -461,12 +461,11 @@ global state. In order to access these variables, you declare them in `extern`
blocks with the `static` keyword: blocks with the `static` keyword:
```rust,no_run ```rust,no_run
# #![feature(libc)] use std::os::raw::c_int;
extern crate libc;
#[link(name = "readline")] #[link(name = "readline")]
extern { extern {
static rl_readline_version: libc::c_int; static rl_readline_version: c_int;
} }
fn main() { fn main() {
@ -480,15 +479,14 @@ interface. To do this, statics can be declared with `mut` so we can mutate
them. them.
```rust,no_run ```rust,no_run
# #![feature(libc)]
extern crate libc;
use std::ffi::CString; use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr; use std::ptr;
#[link(name = "readline")] #[link(name = "readline")]
extern { extern {
static mut rl_prompt: *const libc::c_char; static mut rl_prompt: *const c_char;
} }
fn main() { fn main() {
@ -513,14 +511,13 @@ calling foreign functions. Some foreign functions, most notably the Windows API,
conventions. Rust provides a way to tell the compiler which convention to use: conventions. Rust provides a way to tell the compiler which convention to use:
```rust ```rust
# #![feature(libc)] use std::os::raw::c_int;
extern crate libc;
#[cfg(all(target_os = "win32", target_arch = "x86"))] #[cfg(all(target_os = "win32", target_arch = "x86"))]
#[link(name = "kernel32")] #[link(name = "kernel32")]
#[allow(non_snake_case)] #[allow(non_snake_case)]
extern "stdcall" { extern "stdcall" {
fn SetEnvironmentVariableA(n: *const u8, v: *const u8) -> libc::c_int; fn SetEnvironmentVariableA(n: *const u8, v: *const u8) -> c_int;
} }
# fn main() { } # fn main() { }
``` ```
@ -575,16 +572,45 @@ against `libc` and `libm` by default.
# The "nullable pointer optimization" # The "nullable pointer optimization"
Certain types are defined to not be NULL. This includes references (`&T`, Certain Rust types are defined to never be `null`. This includes references (`&T`,
`&mut T`), boxes (`Box<T>`), and function pointers (`extern "abi" fn()`). `&mut T`), boxes (`Box<T>`), and function pointers (`extern "abi" fn()`). When
When interfacing with C, pointers that might be NULL are often used. interfacing with C, pointers that might be `null` are often used, which would seem to
As a special case, a generic `enum` that contains exactly two variants, one of require some messy `transmute`s and/or unsafe code to handle conversions to/from Rust types.
However, the language provides a workaround.
As a special case, an `enum` that contains exactly two variants, one of
which contains no data and the other containing a single field, is eligible which contains no data and the other containing a single field, is eligible
for the "nullable pointer optimization". When such an enum is instantiated for the "nullable pointer optimization". When such an enum is instantiated
with one of the non-nullable types, it is represented as a single pointer, with one of the non-nullable types listed above, it is represented as a single pointer,
and the non-data variant is represented as the NULL pointer. So and the non-data variant is represented as the null pointer. This is called an
`Option<extern "C" fn(c_int) -> c_int>` is how one represents a nullable "optimization", but unlike other optimizations it is guaranteed to apply to
function pointer using the C ABI. eligible types.
The most common type that takes advantage of the nullable pointer optimization is `Option<T>`,
where `None` corresponds to `null`. So `Option<extern "C" fn(c_int) -> c_int>` is a correct way
to represent a nullable function pointer using the C ABI (corresponding to the C type
`int (*)(int)`). (However, generics are not required to get the optimization. A simple
`enum NullableIntRef { Int(Box<i32>), NotInt }` is also represented as a single pointer.)
Here is an example:
```rust
use std::os::raw::c_int;
/// This fairly useless function receives a function pointer and an integer
/// from C, and returns the result of calling the function with the integer.
/// In case no function is provided, it squares the integer by default.
#[no_mangle]
pub extern fn apply(process: Option<extern "C" fn(c_int) -> c_int>, int: c_int) -> c_int {
match process {
Some(f) => unsafe { f(int) },
None => int * int
}
}
# fn main() {}
```
No `tranmsute` required!
# Calling Rust code from C # Calling Rust code from C
@ -642,12 +668,11 @@ void bar(void *arg);
We can represent this in Rust with the `c_void` type: We can represent this in Rust with the `c_void` type:
```rust ```rust
# #![feature(libc)] use std::os::raw::c_void;
extern crate libc;
extern "C" { extern "C" {
pub fn foo(arg: *mut libc::c_void); pub fn foo(arg: *mut c_void);
pub fn bar(arg: *mut libc::c_void); pub fn bar(arg: *mut c_void);
} }
# fn main() {} # fn main() {}
``` ```