Skip to content

Converting a [string] to a **c_char is subtlely difficult #9564

Closed
@huonw

Description

@huonw

It's currently non-trivial to do a &[&str] -> **c_char conversion correctly; I think there should be a function in std::c_str that handles this (as well as optionally null terminating the **c_char).

v is a &[&str], f is a function taking a **c_char. I've put the types of intermediates for clarity.

Incorrect

a

v.as_imm_buf(|buf: *&str, _| f(buf as **c_char))

The *&str**c_char cast is wrong.

b

let ptrs: ~[*c_char] = v.map(|s: & &str| s.with_c_str(|cstr: *c_char| cstr));
ptrs.as_imm_buf(|buf: **c_char, _| f(buf))

The *c_char from with_c_str shouldn't leave the closure, since it with_c_str allocates an internal buffer. (This is a general problem with methods that pass a raw pointer to a closure: it is extremely easy and tempting to just return that pointer immediately and thus (possibly) create a dangling reference.)

Correct

(at least, I think it is correct)

let c_strs: ~[CString] = v.map(|s: & &str| s.to_c_str());
let mut ptrs: ~[*c_char] = c_strs.map(|c: &CString| c.with_ref(|ptr| ptr));
if null_terminate {
    ptrs.push(std::ptr::null());
}
ptrs.as_imm_buf(|buf: **c_char, _| f(buf))

The c_strs vector must live at least as at least as long as the **c_char is used. In this (unusual) case, returning the *c_char from with_ref is ok (if c_strs is kept alive long enough).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions