| Crates.io | cstring-array |
| lib.rs | cstring-array |
| version | 0.1.1 |
| created_at | 2025-10-18 23:53:59.95661+00 |
| updated_at | 2025-10-19 07:10:12.328894+00 |
| description | Safe, zero-copy wrapper for passing string arrays to C FFI (char**) |
| homepage | https://github.com/RAprogramm/cstring-array |
| repository | https://github.com/RAprogramm/cstring-array |
| max_upload_size | |
| id | 1889751 |
| size | 110,719 |
Safe, zero-copy wrapper for passing string arrays to C FFI (char**)
This crate provides CStringArray, a safe abstraction over C's null-terminated string arrays, commonly used for command-line arguments (argv) and similar purposes.
Vec<CString>, no re-allocation occurschar** pointers with null terminationAdd this to your Cargo.toml:
[dependencies]
cstring-array = "0.1"
use cstring_array::CStringArray;
use std::ffi::c_char;
let args = vec![
"program".to_string(),
"--verbose".to_string(),
"file.txt".to_string(),
];
let array = CStringArray::new(args).unwrap();
// Safe to pass to C FFI functions expecting char**
let ptr: *const *const c_char = array.as_ptr();
assert_eq!(array.len(), 3);
use cstring_array::CStringArray;
use std::ffi::CString;
use std::convert::TryFrom;
// From Vec<String>
let arr1 = CStringArray::new(vec!["foo".to_string(), "bar".to_string()]).unwrap();
// From Vec<CString> (zero-copy)
let cstrings = vec![CString::new("foo").unwrap(), CString::new("bar").unwrap()];
let arr2 = CStringArray::from_cstrings(cstrings).unwrap();
// Using TryFrom with Vec<&str>
let arr3 = CStringArray::try_from(vec!["foo", "bar"]).unwrap();
// Using TryFrom with arrays
let arr4 = CStringArray::try_from(["foo", "bar"]).unwrap();
// Using FromIterator (collect)
let arr5: CStringArray = vec!["a", "b", "c"].into_iter().map(String::from).collect();
CStringArray implements standard Rust traits for idiomatic usage:
use cstring_array::CStringArray;
use std::collections::HashMap;
let arr1 = CStringArray::new(vec!["a".to_string(), "b".to_string()]).unwrap();
// Clone - independent copies
let arr2 = arr1.clone();
assert_eq!(arr1, arr2);
// Equality comparison
assert_eq!(arr1, arr2);
// Hash - use in HashMap/HashSet
let mut map = HashMap::new();
map.insert(arr1.clone(), "value");
// Array indexing
assert_eq!(arr1[0].to_str().unwrap(), "a");
assert_eq!(arr1[1].to_str().unwrap(), "b");
// For loop iteration (borrowed)
for s in &arr1 {
println!("{}", s.to_str().unwrap());
}
// For loop iteration (owned - consumes array)
for s in arr2 {
println!("{}", s.to_str().unwrap());
}
// Functional programming with iterators
let filtered: CStringArray = vec!["a", "bb", "ccc"]
.into_iter()
.filter(|s| s.len() > 1)
.map(String::from)
.collect();
Implemented traits:
Clone - Deep copy with independent memoryPartialEq, Eq - Equality comparisonHash - Use in HashMap and HashSetFromIterator<String> - Collect from String iteratorsFromIterator<CString> - Collect from CString iteratorsIntoIterator - Owned and borrowed iterationIndex<usize> - Array-style indexing arr[0]AsRef<[CString]> - Generic slice conversionuse cstring_array::CStringArray;
use std::ffi::c_char;
extern "C" {
fn execve(
pathname: *const c_char,
argv: *const *const c_char,
envp: *const *const c_char,
) -> i32;
}
fn execute_program(path: &str, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let argv = CStringArray::new(args)?;
let envp = CStringArray::new(vec![])?; // Empty environment
unsafe {
execve(
CString::new(path)?.as_ptr(),
argv.as_ptr(),
envp.as_ptr(),
);
}
Ok(())
}
use cstring_array::{CStringArray, CStringArrayError};
// Interior null bytes are detected
let result = CStringArray::new(vec!["hello\0world".to_string()]);
assert!(matches!(result, Err(CStringArrayError::NulError(_))));
// Empty arrays are not allowed
let result = CStringArray::new(vec![]);
assert!(matches!(result, Err(CStringArrayError::EmptyArray)));
The pointer returned by CStringArray::as_ptr() is only valid for the lifetime of the CStringArray. Ensure the array outlives any C code using the pointer:
use cstring_array::CStringArray;
use std::ffi::c_char;
fn call_c_function(argv: *const *const c_char, argc: i32) {
// ... FFI call ...
}
let array = CStringArray::new(vec!["arg1".to_string(), "arg2".to_string()]).unwrap();
let ptr = array.as_ptr();
call_c_function(ptr, array.len() as i32);
// array must not be dropped before call_c_function returns
CStringArray is designed for zero-cost abstractions:
Vec<CString>Last updated: 2025-10-19 03:04:15 UTC
| Benchmark | Time | Std Dev |
|---|---|---|
| As Ptr | 0 ns | ±0 ns |
| Get | 0 ns | ±0 ns |
| Iter | 317 ns | ±0 ns |
| Try From Vec Str | 4.94 μs | ±15 ns |
| New From Iter | 7.07 μs | ±59 ns |
| Benchmark | Time | Std Dev |
|---|---|---|
| Construction Comparison/Try From Vec Str | 5.22 μs | ±26 ns |
| Construction Comparison/From Vec New | 5.22 μs | ±10 ns |
| Construction Comparison/From Vec String | 5.25 μs | ±19 ns |
| Benchmark | Time | Std Dev |
|---|---|---|
| From Cstrings Zero Copy/10 | 206 ns | ±1 ns |
| From Cstrings Zero Copy/100 | 3.64 μs | ±10 ns |
| From Cstrings Zero Copy/1000 | 35.13 μs | ±59 ns |
| Benchmark | Time | Std Dev |
|---|---|---|
| Large Strings/100 | 375 ns | ±1 ns |
| Large Strings/1000 | 1.50 μs | ±18 ns |
| Large Strings/10000 | 8.03 μs | ±21 ns |
| Benchmark | Time | Std Dev |
|---|---|---|
| New From Strings/10 | 331 ns | ±11 ns |
| New From Strings/100 | 4.97 μs | ±11 ns |
| New From Strings/1000 | 48.51 μs | ±485 ns |
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice represents the number of statements and the coverage, respectively.
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
The top section represents the entire project, proceeding with folders and finally individual files. The size and color of each slice represents the number of statements and the coverage, respectively.
This crate requires Rust 1.90 or later.
Last updated: 2025-10-19 03:27:56 UTC
| Language | Files | Lines | Code | Comments | Blanks |
|---|---|---|---|---|---|
| Rust | 17 | 1831 | 1393 | 91 | 347 |
| TOML | 5 | 223 | 191 | 6 | 26 |
| YAML | 1 | 40 | 28 | 5 | 7 |
| JSON | 1 | 0 | 0 | 0 | 0 |
| Markdown | 4 | 551 | 0 | 377 | 174 |
| Plain Text | 2 | 139 | 0 | 123 | 16 |
| Total | 30 | 2784 | 1612 | 602 | 570 |
std::ffi::CString - Standard library C string typestd::ffi::CStr - Borrowed C string slice