Trying to get the address of the address of the data in a {c}string

I need to create a vector of *mut c_voids. Each one points to a function argument (i am using ffi crate).

Trying to work out how to deal with a cstring.

In c++ I would do

     std::vector<void*> v;
     std::string s = std::string("foo");
     const char* ps = s.c_str();
     v.push_back(static_cast<void*> (const_cast<char**>(&ps)));

I cant work out the rust equivalent

The best way depends on how it's used, if they are read only c syle strings then the first option should work. If they can get modified then I would treat them as a &mut [u8] as in the second option. If the thing consuming them may need to resize them then it gets complicated.

fn rust_cstring_notmut() {
    let s = std::ffi::CString::new("foo").expect("Null byte in string");
    let mut v: Vec<*mut std::ffi::c_void> = vec![];
    // The data is null-terminated, it must not be modified since it comes from a non mutable CString.
    // This would better be a Vec<*const std::ffi::c_void> to make that obvious.
    v.push(s.as_ptr() as _);
}

fn rust_bytes() {
    let mut s = "foo".as_bytes().to_vec();
    let mut v: Vec<*mut std::ffi::c_void> = vec![];
    // The data isn't null-terminated, and any changes to `s` can't change the length.
    // If it should be null-terminated, make sure to add a null byte at the end before pushing the pointer.
    v.push(s.as_mut_ptr() as _);
}```

Thats pushing the address of the string data, I need to push the address of a variable holding the address.

OOPS, I just realized my c++ example is wrong :frowning: will fix

Answering my own question

    let name = c"test.txt";
    let pname = name.as_ptr(); 
    let mut pvec: Vec<*mut c_void> = Vec::new();
    pvec.push(unsafe { mem::transmute::<*const *const i8, *mut c_void>(&pname) });

first, you can use &raw to get a raw pointer; and second, casting a raw pointer don't need unsafe:

    let name = c"test.txt";
    let pname = name.as_ptr(); 
    let mut pvec = vec![];
    pvec.push(&raw const pname as *mut c_void);

I don't why you are doing this, just a reminder: be very careful if you are doing this in a loop, cause you might get a bunch of bad pointers if done incorrectly, e.g. the following code is INCORRECT:

let mut pvec: Vec<*mut c_void> = vec![];
for name in names {
    let pname = name.as_ptr();
    pvec.push(&raw const pname as _);
}
// use the vector as argument to ffi function
// !!! THIS IS WRONG !!!
unsafe { my_ffi_function(pvec.as_ptr(), pvec.len()) };
2 Likes