Converting a Vec<&str> to Vec<&CStr> in rust

Take a look at this function:

fn exec(cli: Vec<&str>) {
    eprintln!("execing: {:?}", cli);
    let args: Vec<&CStr> = cli.iter()
        .map(|s| CString::new(s.as_bytes()).unwrap().as_c_str())
        .collect();
    execv(args[0], &args);
    debug(args);
}

It takes a Vec<&str> and executes it as a command. I'm having trouble converting this to Vec<&CStr> (which is what execv needs). Compiler reports this error for the map operations:

error[E0515]: cannot return value referencing temporary value
   --> src/idea.rs:141:18
    |
141 |         .map(|s| CString::new(s.as_bytes()).unwrap().as_c_str())
    |                  -----------------------------------^^^^^^^^^^^
    |                  |
    |                  returns a value referencing data owned by the current function
    |                  temporary value created here

How do I fix this error?

On this line:

.map(|s| CString::new(s.as_bytes()).unwrap().as_c_str())

You are creating a CString, immediately destroying it, and then you try to put a reference to it in a vector, which fails as you just destroyed the CString.

To fix this, create the vector in two steps:

let args_owned: Vec<CString> = cli.iter()
    .map(|s| CString::new(s.as_bytes()).unwrap())
    .collect();
let args: Vec<&CStr> =  args_owned.iter()
    .map(CString::as_c_str)
    .collect();
2 Likes

This case is where the Rust really shines. Compare it with the equivalent C++ code below

char *foo = std::string("foo").c_str();`

which gives you dangling pointer immediately, and accessing it is UB.

1 Like