6

To be able to use C library, I need to give a *mut c_char parameter to a function. But I don't find a way to have it from a str.

I converted my str to a CString, that's ok, but there's no more way from CString to get a *mut c_char in the nightly build. I found that in 0.12.0 there was a method, but now, what is the process to get that *mut c_char?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Peekmo
  • 2,843
  • 2
  • 19
  • 25
  • Which kind of string do you have, a `&str` or a `String`? – Shepmaster Jan 22 '15 at 17:56
  • Please include the signature of the C function, as it will contain details about how the method is supposed to be called. Specifically, I'm curious how the function knows how much space there is in the string so it doesn't extend past allocated memory. – Shepmaster Jan 22 '15 at 18:02
  • Ideally `CString` would have `DerefMut` implementation with `Output = [c_char]`, so it would be possible to obtain `*mut c_char` just with `c_str.as_mut_ptr()`. But I think that the reason there is no such implementation is that it could break `CString` guarantee that it does not contain zeros inside: `c_str[2] = 0` would be possible in that case. So, I think, your `Vec`-based solution is fine; just don't forget to push a zero byte at the end if your C API needs a zero-terminated string. – Vladimir Matveev Jan 22 '15 at 19:37

6 Answers6

6
let bytes = String::from_str("Test").into_bytes() + b"\0";
let cchars = bytes.map_in_place(|b| b as c_char);
let name: *mut c_char = cchars.as_mut_ptr();

The basic idea is the same as yours but there is no need to slice the Vec explicitly; also a zero byte is appended to the buffer. See also my comment to the question.

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
4

Five years later and none of these solutions are working (for me, at least).

A slightly modified version of this answer:

let string: &str = "Hello, world!";
let bytes: Vec<u8> = String::from(string).into_bytes();
let mut c_chars: Vec<i8> = bytes.iter().map(| c | *c as i8).collect::<Vec<i8>>();

c_chars.push(0); // null terminator

let ptr: *mut c_char = c_chars.as_mut_ptr();
Vadim Hagedorn
  • 159
  • 1
  • 12
2

Now (2022), into_raw() works well for me, for example:

use std::ffi::CString;
use std::os::raw::c_char;
fn main() {    
  let c_string = CString::new("Hello!").expect("CString::new failed");
  let raw: *mut c_char = c_string.into_raw();
}

Check https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw for more information.

Naive
  • 29
  • 3
  • 1
    You need to be careful with into_raw because it will cause a memory leak, unless you convert it back into a CString – jacob_pro Jun 26 '22 at 21:25
1

Look at this piece of documentation, the fn as_mut_ptr() has been moved to the slice part of the API. So you need a mutable slice of type &mut [c_char]. And AFAIK you cannot get that from a CString, those would be read-only.

Instead you can use a mut Vec<c_char>:

let mut x : Vec<c_char> = ...;
let slice = x.as_mut_slice();
let ptr = slice.as_mut_ptr();
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
rodrigo
  • 94,151
  • 12
  • 143
  • 190
0

Thanks to rodrigo's post, I found a way (very dirty, but it works) to solve my problem. Here is the code :

let mut string = std::string::String::from_str("Test");
let bytes = string.into_bytes();
let mut cchar : Vec<c_char> = bytes.map_in_place(|w| w as c_char);
let slice = cchar.as_mut_slice();
let name: *mut c_char = slice.as_mut_ptr();

A bit complex in my opinion

Peekmo
  • 2,843
  • 2
  • 19
  • 25
  • Nice, but I think you can omit the `map_in_place`. Maybe the compiler is able to optimize out that code, because a `u8` and a `c_char` (aka `i8`) are guaranteed to be bit-identical, IIANM. – rodrigo Jan 22 '15 at 21:12
  • No, it does not compile without the map_in_place ;) – Peekmo Jan 22 '15 at 22:29
  • What I mean is that you can declare `name : *mut u8` and change the API import code accordingly and all should just work. Or maybe you could `transmute()` it, but I don't know the exact rules. – rodrigo Jan 22 '15 at 23:51
0

If you want to

let mut hello =   b"Hello World\0".to_vec().as_mut_ptr() as *mut i8;
Reza
  • 1,478
  • 1
  • 15
  • 25