Rust functions with string arguments

Let’s start on something a little more complex, accepting strings as
arguments. In Rust, strings are composed of a slice of u8 and are
guaranteed to be valid UTF-8, which allows for NUL bytes in the
interior of the string. In C, strings are just pointers to a char
and are terminated by a NUL byte (with the integer value 0). Some
work is needed to convert between these two representations.

We have to ensure that the C pointer is not NULL as Rust
references are not allowed to be NULL.

Use std::ffi::CStr to wrap the pointer. CStr will
compute the length of the string based on the terminating NUL. This
requires an unsafe block as we will be dereferencing a raw pointer,
which the Rust compiler cannot verify meets all the safety guarantees
so the programmer must do it instead.

Ensure that the C string is valid UTF-8 and convert it to a Rust
string slice.

Use the string slice.

In this example, we are simply aborting the program if any of our
preconditions fail. Each use case must evaluate what are appropriate
failure modes, but failing loudly and early is a good initial
position.

Ownership and lifetimes

In this example, the Rust code does not own the string slice, and
the compiler will only allow the string to live as long as the CStr
instance. It is up to the programmer to ensure that this lifetime is
sufficiently short.

The Foreign.C.String module has support for converting Haskell’s
string representation to C’s packed-byte representation. We can
create one with the newCString function, and then pass the
CString value to our foreign call.

Julia strings (of base type AbstractString) are automatically
converted to C strings. The Cstring type from Julia is compatible
with the Rust type CStr, as it also assumes a NUL terminator byte
and does not allow NUL bytes embedded in the string.