Skip to content

Commit 032ea41

Browse files
committed
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.
1 parent a373b84 commit 032ea41

File tree

1 file changed

+46
-21
lines changed

1 file changed

+46
-21
lines changed

src/doc/book/ffi.md

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -461,12 +461,11 @@ global state. In order to access these variables, you declare them in `extern`
461461
blocks with the `static` keyword:
462462

463463
```rust,no_run
464-
# #![feature(libc)]
465-
extern crate libc;
464+
use std::os::raw::c_int;
466465
467466
#[link(name = "readline")]
468467
extern {
469-
static rl_readline_version: libc::c_int;
468+
static rl_readline_version: c_int;
470469
}
471470
472471
fn main() {
@@ -480,15 +479,14 @@ interface. To do this, statics can be declared with `mut` so we can mutate
480479
them.
481480

482481
```rust,no_run
483-
# #![feature(libc)]
484-
extern crate libc;
485482
486483
use std::ffi::CString;
484+
use std::os::raw::c_char;
487485
use std::ptr;
488486
489487
#[link(name = "readline")]
490488
extern {
491-
static mut rl_prompt: *const libc::c_char;
489+
static mut rl_prompt: *const c_char;
492490
}
493491
494492
fn main() {
@@ -513,14 +511,13 @@ calling foreign functions. Some foreign functions, most notably the Windows API,
513511
conventions. Rust provides a way to tell the compiler which convention to use:
514512

515513
```rust
516-
# #![feature(libc)]
517-
extern crate libc;
514+
use std::os::raw::c_int;
518515

519516
#[cfg(all(target_os = "win32", target_arch = "x86"))]
520517
#[link(name = "kernel32")]
521518
#[allow(non_snake_case)]
522519
extern "stdcall" {
523-
fn SetEnvironmentVariableA(n: *const u8, v: *const u8) -> libc::c_int;
520+
fn SetEnvironmentVariableA(n: *const u8, v: *const u8) -> c_int;
524521
}
525522
# fn main() { }
526523
```
@@ -575,16 +572,45 @@ against `libc` and `libm` by default.
575572

576573
# The "nullable pointer optimization"
577574

578-
Certain types are defined to not be NULL. This includes references (`&T`,
579-
`&mut T`), boxes (`Box<T>`), and function pointers (`extern "abi" fn()`).
580-
When interfacing with C, pointers that might be NULL are often used.
581-
As a special case, a generic `enum` that contains exactly two variants, one of
575+
Certain Rust types are defined to never be `null`. This includes references (`&T`,
576+
`&mut T`), boxes (`Box<T>`), and function pointers (`extern "abi" fn()`). When
577+
interfacing with C, pointers that might be `null` are often used, which would seem to
578+
require some messy `transmute`s and/or unsafe code to handle conversions to/from Rust types.
579+
However, the language provides a workaround.
580+
581+
As a special case, an `enum` that contains exactly two variants, one of
582582
which contains no data and the other containing a single field, is eligible
583583
for the "nullable pointer optimization". When such an enum is instantiated
584-
with one of the non-nullable types, it is represented as a single pointer,
585-
and the non-data variant is represented as the NULL pointer. So
586-
`Option<extern "C" fn(c_int) -> c_int>` is how one represents a nullable
587-
function pointer using the C ABI.
584+
with one of the non-nullable types listed above, it is represented as a single pointer,
585+
and the non-data variant is represented as the null pointer. This is called an
586+
"optimization", but unlike other optimizations it is guaranteed to apply to
587+
eligible types.
588+
589+
The most common type that takes advantage of the nullable pointer optimization is `Option<T>`,
590+
where `None` corresponds to `null`. So `Option<extern "C" fn(c_int) -> c_int>` is a correct way
591+
to represent a nullable function pointer using the C ABI (corresponding to the C type
592+
`int (*)(int)`). (However, generics are not required to get the optimization. A simple
593+
`enum NullableIntRef { Int(Box<i32>), NotInt }` is also represented as a single pointer.)
594+
595+
Here is an example:
596+
597+
```rust
598+
use std::os::raw::c_int;
599+
600+
/// This fairly useless function receives a function pointer and an integer
601+
/// from C, and returns the result of calling the function with the integer.
602+
/// In case no function is provided, it squares the integer by default.
603+
#[no_mangle]
604+
pub extern fn apply(process: Option<extern "C" fn(c_int) -> c_int>, int: c_int) -> c_int {
605+
match process {
606+
Some(f) => unsafe { f(int) },
607+
None => int * int
608+
}
609+
}
610+
# fn main() {}
611+
```
612+
613+
No `tranmsute` required!
588614

589615
# Calling Rust code from C
590616

@@ -642,12 +668,11 @@ void bar(void *arg);
642668
We can represent this in Rust with the `c_void` type:
643669
644670
```rust
645-
# #![feature(libc)]
646-
extern crate libc;
671+
use std::os::raw::c_void;
647672
648673
extern "C" {
649-
pub fn foo(arg: *mut libc::c_void);
650-
pub fn bar(arg: *mut libc::c_void);
674+
pub fn foo(arg: *mut c_void);
675+
pub fn bar(arg: *mut c_void);
651676
}
652677
# fn main() {}
653678
```

0 commit comments

Comments
 (0)