From 032ea41e99b39f6af2aa26c0ba049d0d215d8ebb Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 13 Jun 2016 13:22:38 -0400 Subject: [PATCH 01/12] 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. --- src/doc/book/ffi.md | 67 +++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 3cbe5d6026774..873d078f053e4 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -461,12 +461,11 @@ global state. In order to access these variables, you declare them in `extern` blocks with the `static` keyword: ```rust,no_run -# #![feature(libc)] -extern crate libc; +use std::os::raw::c_int; #[link(name = "readline")] extern { - static rl_readline_version: libc::c_int; + static rl_readline_version: c_int; } fn main() { @@ -480,15 +479,14 @@ interface. To do this, statics can be declared with `mut` so we can mutate them. ```rust,no_run -# #![feature(libc)] -extern crate libc; use std::ffi::CString; +use std::os::raw::c_char; use std::ptr; #[link(name = "readline")] extern { - static mut rl_prompt: *const libc::c_char; + static mut rl_prompt: *const c_char; } 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: ```rust -# #![feature(libc)] -extern crate libc; +use std::os::raw::c_int; #[cfg(all(target_os = "win32", target_arch = "x86"))] #[link(name = "kernel32")] #[allow(non_snake_case)] 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() { } ``` @@ -575,16 +572,45 @@ against `libc` and `libm` by default. # The "nullable pointer optimization" -Certain types are defined to not be NULL. This includes references (`&T`, -`&mut T`), boxes (`Box`), and function pointers (`extern "abi" fn()`). -When interfacing with C, pointers that might be NULL are often used. -As a special case, a generic `enum` that contains exactly two variants, one of +Certain Rust types are defined to never be `null`. This includes references (`&T`, +`&mut T`), boxes (`Box`), and function pointers (`extern "abi" fn()`). When +interfacing with C, pointers that might be `null` are often used, which would seem to +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 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, -and the non-data variant is represented as the NULL pointer. So -`Option c_int>` is how one represents a nullable -function pointer using the C ABI. +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. This is called an +"optimization", but unlike other optimizations it is guaranteed to apply to +eligible types. + +The most common type that takes advantage of the nullable pointer optimization is `Option`, +where `None` corresponds to `null`. So `Option 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), 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 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 @@ -642,12 +668,11 @@ void bar(void *arg); We can represent this in Rust with the `c_void` type: ```rust -# #![feature(libc)] -extern crate libc; +use std::os::raw::c_void; extern "C" { - pub fn foo(arg: *mut libc::c_void); - pub fn bar(arg: *mut libc::c_void); + pub fn foo(arg: *mut c_void); + pub fn bar(arg: *mut c_void); } # fn main() {} ``` From 48ce20653a470f2d4734fb0ee4a89905da23b15c Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 13 Jun 2016 15:05:22 -0400 Subject: [PATCH 02/12] generics-agnostic description --- src/doc/book/ffi.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 873d078f053e4..590c23e89299e 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -578,19 +578,17 @@ interfacing with C, pointers that might be `null` are often used, which would se 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 -for the "nullable pointer optimization". When such an enum is instantiated -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. This is called an -"optimization", but unlike other optimizations it is guaranteed to apply to +As a special case, an `enum` is eligible for the "nullable pointer optimization" if it +contains exactly two variants, one of which contains no data and the other contains +a single field of one of the non-nullable types listed above. This means it is represented +as a single pointer, and the non-data variant is represented as the null pointer. This is +called an "optimization", but unlike other optimizations it is guaranteed to apply to eligible types. The most common type that takes advantage of the nullable pointer optimization is `Option`, where `None` corresponds to `null`. So `Option 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), NotInt }` is also represented as a single pointer.) +`int (*)(int)`). Here is an example: From cc2fc48dec9ae582ebba9761185afc5e21bf47b6 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 13 Jun 2016 16:13:20 -0400 Subject: [PATCH 03/12] expand nullable pointer example --- src/doc/book/ffi.md | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 590c23e89299e..fb8896da86d73 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -590,22 +590,42 @@ where `None` corresponds to `null`. So `Option c_int>` i to represent a nullable function pointer using the C ABI (corresponding to the C type `int (*)(int)`). -Here is an example: +Here is a contrived example. Let's say some C library has a facility for registering a +callback, which gets called in certain situations. The callback is passed a function pointer +and an integer and it is supposed to run the function with the integer as a parameter. So +we have function pointers flying across the FFI interface in both directions. ```rust use std::os::raw::c_int; +extern "C" { + /// Register the callback. + fn register(Option c_int>, c_int) -> 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 c_int>, int: c_int) -> c_int { +extern "C" fn apply(process: Option c_int>, int: c_int) -> c_int { match process { Some(f) => unsafe { f(int) }, None => int * int } } -# fn main() {} + +fn main() { + unsafe { + register(Some(apply)); + } +} +``` + +And the code on the C side looks like this: + +```c +void register(void (*f)(void (*)(int), int)) { + ... +} ``` No `tranmsute` required! From 84366b6f281614f61381962e2e46b73bb471c737 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 13 Jun 2016 17:47:18 -0400 Subject: [PATCH 04/12] recursion --- src/doc/book/ffi.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index fb8896da86d73..5a2ec86c12f56 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -580,10 +580,10 @@ However, the language provides a workaround. As a special case, an `enum` is eligible for the "nullable pointer optimization" if it contains exactly two variants, one of which contains no data and the other contains -a single field of one of the non-nullable types listed above. This means it is represented -as a single pointer, and the non-data variant is represented as the null pointer. This is -called an "optimization", but unlike other optimizations it is guaranteed to apply to -eligible types. +a single field of one of the non-nullable types listed above (or a struct containing such a type). +This means it is represented as a single pointer, and the non-data variant is represented as a +null pointer. This is called an "optimization", but unlike other optimizations it is guaranteed +to apply to eligible types. The most common type that takes advantage of the nullable pointer optimization is `Option`, where `None` corresponds to `null`. So `Option c_int>` is a correct way From 0016af5f13702feb92f6323417fb9fe495e964d0 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 13 Jun 2016 17:54:39 -0400 Subject: [PATCH 05/12] not just a single field --- src/doc/book/ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 5a2ec86c12f56..07b8d8e0da8a9 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -580,7 +580,7 @@ However, the language provides a workaround. As a special case, an `enum` is eligible for the "nullable pointer optimization" if it contains exactly two variants, one of which contains no data and the other contains -a single field of one of the non-nullable types listed above (or a struct containing such a type). +a field of one of the non-nullable types listed above (or a struct containing such a type). This means it is represented as a single pointer, and the non-data variant is represented as a null pointer. This is called an "optimization", but unlike other optimizations it is guaranteed to apply to eligible types. From 1cceca8dfdda4a8bf3daa6a8c23adf7f23acd81a Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 13 Jun 2016 18:00:07 -0400 Subject: [PATCH 06/12] foreign function interface interface --- src/doc/book/ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 07b8d8e0da8a9..4a55db38d3814 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -593,7 +593,7 @@ to represent a nullable function pointer using the C ABI (corresponding to the C Here is a contrived example. Let's say some C library has a facility for registering a callback, which gets called in certain situations. The callback is passed a function pointer and an integer and it is supposed to run the function with the integer as a parameter. So -we have function pointers flying across the FFI interface in both directions. +we have function pointers flying across the FFI boundary in both directions. ```rust use std::os::raw::c_int; From fae33352721c4b4d6fbdc66520043ae721121d77 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 13 Jun 2016 21:10:12 -0400 Subject: [PATCH 07/12] extern fns require named parameters Not sure the example is going to stay, but I can try to pass Travis for the bragging rights. --- src/doc/book/ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 4a55db38d3814..74c99273fa262 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -600,7 +600,7 @@ use std::os::raw::c_int; extern "C" { /// Register the callback. - fn register(Option c_int>, c_int) -> c_int>); + fn register(cb: Option c_int>, c_int) -> c_int>); } /// This fairly useless function receives a function pointer and an integer From 5276b2967060d749e20674a08a2438a24f0f7b07 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 13 Jun 2016 21:38:04 -0400 Subject: [PATCH 08/12] change confusing wording about discriminant --- src/doc/book/ffi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 74c99273fa262..e63516e58ccfd 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -581,9 +581,9 @@ However, the language provides a workaround. As a special case, an `enum` is eligible for the "nullable pointer optimization" if it contains exactly two variants, one of which contains no data and the other contains a field of one of the non-nullable types listed above (or a struct containing such a type). -This means it is represented as a single pointer, and the non-data variant is represented as a -null pointer. This is called an "optimization", but unlike other optimizations it is guaranteed -to apply to eligible types. +This means no extra space is required for a discriminant; rather, the empty variant is represented +by putting a `null` value into the non-nullable field. This is called an "optimization", but unlike +other optimizations it is guaranteed to apply to eligible types. The most common type that takes advantage of the nullable pointer optimization is `Option`, where `None` corresponds to `null`. So `Option c_int>` is a correct way From 54ecc210ec720e93c6e493e6fc3e7464cd22002f Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Tue, 14 Jun 2016 15:53:55 -0400 Subject: [PATCH 09/12] hack to make example compile --- src/doc/book/ffi.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index e63516e58ccfd..e1b9789a3144a 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -598,17 +598,21 @@ we have function pointers flying across the FFI boundary in both directions. ```rust use std::os::raw::c_int; +# #[cfg(hidden)] extern "C" { /// Register the callback. fn register(cb: Option c_int>, c_int) -> c_int>); } +# unsafe fn register(_: Option c_int>, +# c_int) -> 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. extern "C" fn apply(process: Option c_int>, int: c_int) -> c_int { match process { - Some(f) => unsafe { f(int) }, + Some(f) => f(int), None => int * int } } From 8e7abea93e84af6060ce6eeb530eb82fed3cff60 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 27 Jul 2016 13:51:48 -0400 Subject: [PATCH 10/12] revert libc changes --- src/doc/book/ffi.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index e1b9789a3144a..983bd46a0c9e6 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -461,11 +461,12 @@ global state. In order to access these variables, you declare them in `extern` blocks with the `static` keyword: ```rust,no_run -use std::os::raw::c_int; +# #![feature(libc)] +extern crate libc; #[link(name = "readline")] extern { - static rl_readline_version: c_int; + static rl_readline_version: libc::c_int; } fn main() { @@ -479,14 +480,15 @@ interface. To do this, statics can be declared with `mut` so we can mutate them. ```rust,no_run +# #![feature(libc)] +extern crate libc; use std::ffi::CString; -use std::os::raw::c_char; use std::ptr; #[link(name = "readline")] extern { - static mut rl_prompt: *const c_char; + static mut rl_prompt: *const libc::c_char; } fn main() { @@ -511,13 +513,14 @@ calling foreign functions. Some foreign functions, most notably the Windows API, conventions. Rust provides a way to tell the compiler which convention to use: ```rust -use std::os::raw::c_int; +# #![feature(libc)] +extern crate libc; #[cfg(all(target_os = "win32", target_arch = "x86"))] #[link(name = "kernel32")] #[allow(non_snake_case)] extern "stdcall" { - fn SetEnvironmentVariableA(n: *const u8, v: *const u8) -> c_int; + fn SetEnvironmentVariableA(n: *const u8, v: *const u8) -> libc::c_int; } # fn main() { } ``` @@ -690,11 +693,12 @@ void bar(void *arg); We can represent this in Rust with the `c_void` type: ```rust -use std::os::raw::c_void; +# #![feature(libc)] +extern crate libc; extern "C" { - pub fn foo(arg: *mut c_void); - pub fn bar(arg: *mut c_void); + pub fn foo(arg: *mut libc::c_void); + pub fn bar(arg: *mut libc::c_void); } # fn main() {} ``` From 1319b293c67f0fa8e9504c692898d2cee1866a9a Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 27 Jul 2016 13:52:13 -0400 Subject: [PATCH 11/12] fix typo --- src/doc/book/ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 983bd46a0c9e6..f9c50f0399add 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -635,7 +635,7 @@ void register(void (*f)(void (*)(int), int)) { } ``` -No `tranmsute` required! +No `transmute` required! # Calling Rust code from C From 29546dd06d733d065fc497902bbcecbbb06ce621 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 27 Jul 2016 13:57:14 -0400 Subject: [PATCH 12/12] remove claim about searching through nested fields for the nullable type, even though that is how it works --- src/doc/book/ffi.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index f9c50f0399add..ca104ff29ace3 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -581,12 +581,12 @@ interfacing with C, pointers that might be `null` are often used, which would se 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` is eligible for the "nullable pointer optimization" if it -contains exactly two variants, one of which contains no data and the other contains -a field of one of the non-nullable types listed above (or a struct containing such a type). -This means no extra space is required for a discriminant; rather, the empty variant is represented -by putting a `null` value into the non-nullable field. This is called an "optimization", but unlike -other optimizations it is guaranteed to apply to eligible types. +As a special case, an `enum` is eligible for the "nullable pointer optimization" if it contains +exactly two variants, one of which contains no data and the other contains a field of one of the +non-nullable types listed above. This means no extra space is required for a discriminant; rather, +the empty variant is represented by putting a `null` value into the non-nullable field. This is +called an "optimization", but unlike other optimizations it is guaranteed to apply to eligible +types. The most common type that takes advantage of the nullable pointer optimization is `Option`, where `None` corresponds to `null`. So `Option c_int>` is a correct way @@ -599,7 +599,9 @@ and an integer and it is supposed to run the function with the integer as a para we have function pointers flying across the FFI boundary in both directions. ```rust -use std::os::raw::c_int; +# #![feature(libc)] +extern crate libc; +use libc::c_int; # #[cfg(hidden)] extern "C" {