9

I have a function

extern "C" {
    fn log_impl(ptr: *const u8);
}

fn log(s: &str) {
    log_impl(s.as_bytes() as *const u8);
}

This gives me the following error:

error[E0606]: casting `&[u8]` as `*const u8` is invalid
 --> src/main.rs:6:14
  |
6 |     log_impl(s.as_bytes() as *const u8);
  |              ^^^^^^^^^^^^^^^^^^^^^^^^^

The most similar question to what I'm trying to do is Converting a str to a &[u8].

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
lee
  • 740
  • 2
  • 11
  • 24
  • 1
    I would check out the FFI module. For example there is https://doc.rust-lang.org/std/ffi/struct.CString.html – squiguy Mar 10 '18 at 00:19

2 Answers2

18

Rust strings are not NUL-terminated like most C functions expect. You can convert a &str to *const u8 by using &s.as_bytes()[0] as *const u8 or by using s.as_ptr(), but that will not be valid to pass to any C function expecting a NUL-terminated string.

Instead, you probably need to use CString which will copy the string to a buffer and add a NUL terminator to the end. Here is an example assuming that log_impl doesn't store a pointer to the string:

fn log(s: &str) {
    unsafe {
        let c_str = CString::new(s).unwrap();
        log_impl(c_str.as_ptr() as *const u8);
    }
}
Jordan Miner
  • 2,034
  • 17
  • 19
  • 4
    This function probably doesn't need to be marked as `unsafe` and should instead use an `unsafe` block internally. – Shepmaster Mar 10 '18 at 01:59
  • 1
    I agree. I just did that since it made the example shorter and it could still be copied-and-pasted to the Rust playground. I edited it to add the unsafe block instead. – Jordan Miner Mar 12 '18 at 20:52
  • Asking as a rust noob: assuming the `&str` originally comes from some `String`, could we pre-load the original `String` with `s.push("\0")`? Would this work or am I missing something? – Jay Sullivan Jul 12 '21 at 18:10
  • @JaySullivan Yes, doing that would work and would be more efficient than using `CString` if the `String` has capacity for the 0 byte without reallocating. `CString` just does two things: checks that there are no 0 bytes in the middle of the string and adds a 0 byte to the end (which means it normally has to copy the whole string). – Jordan Miner Jul 30 '21 at 02:20
  • To avoid unnecessary heap allocations, instead of `CString`, I use [a function I wrote](https://github.com/jminer/clear-coat/blob/068f247ce84017583cc49a257d84e659137e6c4f/src/attributes.rs#L17) that checks if a `&str` already ends with `\0`, and if so, it uses it as-is, falling back to copying it to the stack if it's small enough, falling back to copying it to the heap (with the stack vs heap handled by the [`SmallVec`](https://docs.rs/smallvec/) library). – Jordan Miner Jul 30 '21 at 02:21
1

&[u8] is called a slice in Rust, and *u8 is a raw pointer. You can go back and forth between the two types using from_ptr() and as_ptr().

extern "C" {
    fn log_impl(ptr: *const u8);
}

fn log(s: &str) {
    log_impl(s.as_bytes().as_ptr() as *const u8);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
lee
  • 740
  • 2
  • 11
  • 24