在FFI与C交互中,少不了与C中字符串交互。在Rust中,有
各种String存在的意义:
OsString:因为要与操作系统等复杂的世界交互;
因为Rust世界中的Strings 始终是有效的 UTF-8。对于非 UTF-8 字符串,可以用到OsString。
CString: 与C的世界进行交互;
String:在Rust的世界中交互;
一、CString、String等代码探析
use std::ffi::{CStr, CString,c_char};
use std::borrow::Cow;
fn main() {
println!("Hello, world!");
show_cstring_bytes(CString::new("Hello, world!").expect("CString::new failed"));
show_string_bytes("Hello, world!".to_string());
}
// as:不consume
// from/into:consume ownship
// into_bytes(),as_bytes()返回的缓冲区不包含尾随 nul 终止符,并且保证不包含任何内部 nul 字节。
// 必须用as_bytes_with_nul()返回的缓冲区包含 nul 终止符。
fn show_cstring_bytes_no_null(s:CString){
let c_string_bytes = s.as_bytes();
println!("c_string_bytes no null : {:?} len: {:?}", c_string_bytes,c_string_bytes.len());
}
fn show_cstring_bytes(s:CString){
let c_string_bytes = s.as_bytes_with_nul();
println!("c_string_bytes with null : {:?} len: {:?}", c_string_bytes,c_string_bytes.len());
}
fn show_string_bytes(s:String){
let string_bytes = s.into_bytes();
println!(" string_bytes : {:?} len :{:?}", string_bytes,string_bytes.len());
}
// CString ->&CStr
fn cstring_to_cstr(s:&CString) ->&CStr{
s.as_c_str()
}
fn show_cstr_bytes_no_null(s:&CStr){
let c_str_bytes = s.to_bytes();
println!("c_str_bytes no null: {:?} len: {:?}", c_str_bytes,c_str_bytes.len());
}
fn show_cstr_bytes_with_null(s:&CStr){
let c_str_bytes = s.to_bytes_with_nul();
println!("c_str_bytes with null: {:?} len: {:?}", c_str_bytes,c_str_bytes.len());
}
fn cstring_to_str(s:&CString) ->&str{
s.to_str().expect("CString to str failed")
}
fn cstr_to_cstring(s:&CStr) ->CString{
s.to_owned()
}
// *const c_char具体是*const i8 还是 *const u8由平台决定。
fn get_ptr_c_char() ->*const c_char{
const BYTES: &[u8] = b"Hello, world! to c_char!\0";
//或是:BYTES.as_ptr().cast()
BYTES.as_ptr() as *const _
}
fn get_cstr_from_bytes<'a>() ->&'a CStr{
const BYTES_: &[u8] = b"Hello, world! from bytes!\0";
let cstr = CStr::from_bytes_with_nul(BYTES_).expect("CStr::from_bytes_with_nul failed");
cstr
}
fn get_cstr_from_ptr_c_char<'a>(s:*const c_char) ->&'a CStr{
unsafe { CStr::from_ptr(s) }
}
fn get_cstring() ->CString{
let c_string = CString::new("Hello, world! from c string!").expect("CString::new failed");
c_string
}
fn check_cstring(s: *const c_char) -> bool{
unsafe {
let slice = CStr::from_ptr(s);
let my_str = slice.to_str();
match my_str{
Ok(_) => return true,
Err(_) => return false,
};
//println!("my_str: {}", my_str.expect("CStr::from_ptr failed"));
}
}
fn cstr_to_str(s:&CStr) ->&str{
s.to_str().expect("CStr::from_ptr failed")
}
fn cstring_to_cow_str(s:&CString) ->Cow<'_,str>{
//let c_string = CString::new("Hello, world! from c string!").expect("CString::new failed");
let c_string_ptr = s.as_ptr();
let cow = unsafe { CStr::from_ptr(c_string_ptr).to_string_lossy() }; // COW<'_,str>
cow
}
fn cstr_to_cow_str(s:&CStr) ->Cow<'_,str>{
s.to_string_lossy()
}
fn cstring_to_box_cstr(s:CString) ->Box<CStr>{
s.into_boxed_c_str()
}
fn box_cstr_to_cstring(s:Box<CStr>) ->CString{
s.into_c_string()
}
fn vec_u8_with_null_to_cstring_unchecked(v:Vec<u8>) ->CString{
unsafe{CString::from_vec_with_nul_unchecked(v)}
}
fn vec_u8_with_null_to_cstring_checked(v:Vec<u8>) ->CString{
CString::from_vec_with_nul(v).expect("CString::from_vec_with_nul failed")
}
fn vec_u8_no_null_to_cstring(v:Vec<u8>) ->CString{
unsafe{CString::from_vec_unchecked(v)}
}
fn bytes_with_null_to_cstr_unchecked(bytes:&[u8]) ->&CStr{
unsafe{ CStr::from_bytes_with_nul_unchecked(bytes) }
}
fn bytes_with_null_to_cstr_check(bytes:&[u8]) ->&CStr{
unsafe{ CStr::from_bytes_with_nul(bytes).unwrap() }
}
fn bytes_no_null_to_cstr(bytes:&[u8]) ->&CStr{
unsafe{ CStr::from_bytes_until_nul(bytes).unwrap() }
}
// MUST *mut : move ownership
fn ptr_to_cstring(ptr:*mut c_char) ->CString{
unsafe{ CString::from_raw(ptr) }
}
// MUST:*mut : consume ownership
fn cstring_to_ptr_with_consume(s:CString) ->*mut c_char{
s.into_raw() // s 被消耗,不能再使用
}
fn cstring_to_ptr_no_consume(s:&CString) ->*const c_char{
s.as_ptr()
}
fn ptr_to_cstr<'a>(ptr: *const i8) ->&'a CStr{
unsafe{ CStr::from_ptr(ptr) }
}
fn cstring_to_string(s:CString) ->String{
// let c_string_ptr = s.as_ptr();
// let my_string = unsafe { CStr::from_ptr(c_string_ptr).to_string_lossy() }; // COW<'_,str>
// println!("my_string: {}", my_string);
s.into_string().unwrap() // 消耗s,不能再使用
}
fn string_to_cstring(s: String) ->CString{
let c_string = CString::new(&*s).expect("CString::new failed");
c_string
}
二、输出结果
Hello, world!
c_string_bytes with null : [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 0] len: 14
string_bytes : [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] len :13
可以看到,在CString和String转化为字节后的本质区别。
相关的转化具体见上面的代码,有助于加深认识。