Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I convert Rust `Args` into the argc and argv C equivalents?

Tags:

c

rust

I am using a C API (in particular MPI_Init) that needs int argc, char **argv. I am trying to generate an equivalent argc, argv with the following code:

let argc = std::env::args().len() as c_int;
let c_strs: ~[CString] = std::env:args().map(|s: & &str| s.to_c_str());
let mut argv: ~[*c_char] = c_strs.map(|c: &CString| c.with_ref(|ptr| ptr));
if null_terminate {
    argv.push(std::ptr::null());
}

By adapting this discussion on Github.

It fails with:

error: expected type, found `~`
src/lib.rs:37   let c_strs: ~[CString] = std::env::args().map(|s: & &str| s.to_c_str());
                            ^

I got rid of the ~ and then it could not find to_c_str() and was unsure what to replace to_c_str with, to_raw() (for instance) failed.

Does anyone know of a way to convert Args to a more C friendly format?

like image 267
Dair Avatar asked Dec 20 '15 09:12

Dair


1 Answers

My answer works with the current stable Rust (1.5) and probably with beta and nightly.

The following Rust code calls the foo(argc, argv) function implemented in C. The signature of foo is very similar to a main function.

extern crate libc;

use libc::c_char;
use libc::c_int;

use std::ffi::CString;

#[link(name="foo")]
extern "C" {
    fn foo(argc: c_int, argv: *const *const c_char);
}

fn main() {
    // create a vector of zero terminated strings
    let args = std::env::args().map(|arg| CString::new(arg).unwrap() ).collect::<Vec<CString>>();
    // convert the strings to raw pointers
    let c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const c_char>>();
    unsafe {
        // pass the pointer of the vector's internal buffer to a C function
        foo(c_args.len() as c_int, c_args.as_ptr());
    };
}

Note, that the C side only borrows the pointers to the strings. If you want to store them, use strdup() on them.

I also used unwrap() on CString instances. If your string contains 0 bytes, it will return an error, see https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#method.new.

Proof:

I put this code into a cargo project, and added libc as a dependency. The foo() function looks like this:

#include <stdio.h>

void foo(int argc, char* argv[]) {
    int i;

    for (i = 0; i < argc; i++) {
        printf("argv[%d]: %s\n", i, argv[i]);
    }
}

I compiled this code with:

gcc foo.c -o libfoo.so -shared -fPIC

Then copied libfoo.so under target/debug/deps (just to be in the library search path). Then I run my cargo project:

$ cargo run the quick brown fox
   Compiling args v0.1.0 (file:///home/tibi/Codes/Rust/argv/args)
     Running `target/debug/args the quick brown fox`
argv[0]: target/debug/args
argv[1]: the
argv[2]: quick
argv[3]: brown
argv[4]: fox
like image 138
Tibor Benke Avatar answered Oct 17 '22 17:10

Tibor Benke