如何用字符串更新 libc::c_char 数组?

How to update libc::c_char array with String?

我已经写了一些 FFI 代码到 C/C++,

use libc::c_char;
use std::ffi::CString;

type arr_type = [c_char; 20]; // arr_type is the type in C
let mut arr : arr_type = [0; 20]; 

let s = "happy123";
let c_s = CString::new(s).unwrap();
let s_ptr = c_s.as_ptr();

如何使用字符串 s 更新 arr?在 C/C++ 中,我可以使用 memcpystrcpy 等...

我尝试了很多,比如使用 rlibc::memcpy 并发现它不能与 libc.. 一起使用,但是编译器不让我通过,Rust 中关于数组的信息很少。


添加: 看完回复,我想补充一些信息和更多的问题。

1.

在C++中,我使用strcpy_s将字符串复制到char数组,因为字符串的长度和数组的大小都是已知的。

下面两种方法我都试过了。

std::iter::Zip方法,看起来很像strcpy_s,但我不知道是否有一些性能影响。

copy_nonoverlapping 方法,它使用 as_mut_ptr() 将数组转换为指针然后没有长度信息,因为它在 unsafe { } 块中,我试过复制一个字符串比数组长而且没有错误显示...我想知道这样可以吗?

Rust 中是否有类似于 C++ 中的 strcpy_s 的函数?

2.

我正在使用 windows 和 msvc,对于字符数组,我的意思是 not deal with encoding 或使用 default codepage encoding.

以下在源文件中都可以:

C++:

std::string s = "world is 世界";
std::wstring ws = L"world is 世界";

Qt:

QString qs = QStringLiteral("world is 世界");

Python 3:

s = 'world is 世界'

但是在 Rust 中,下面可能是错误的?正如我在 Eclipse Debug window.

中看到的
let s = "world is 世界";

我找到了 rust-encoding 并尝试了这些:

use encoding::{Encoding, EncoderTrap};
use encoding::all::GB18030;

let s = "world is 世界";  
let enc = GB18030.encode(&s , EncoderTrap::Strict);

Rust 有更好的方法吗?

我建议通过同时迭代数组和字符串并将字符串字符分配给数组字符来单独更新每个字符。我将最后的 [=12=] 添加到 Rust 字符串中。

#![feature(libc)]
extern crate libc;

fn main() {
    use libc::c_char;

    type ArrType = [c_char; 20]; // arr_type is the type in C
    let mut arr : ArrType = [0; 20]; 

    let s = "happy123[=10=]";
    assert!(s.len() <= arr.len());
    for (a, c) in arr.iter_mut().zip(s.bytes()) {
        *a = c as i8;
    }
}

Try it out in the PlayPen


在大多数情况下,llvm 会将循环优化为 memcopy。

define internal void @_ZN4main20hf4c098c7157f3263faaE() unnamed_addr #0 {
entry-block:
  %0 = alloca %"2.core::str::Bytes", align 8
  %arg4 = alloca %str_slice, align 8
  %1 = bitcast %"2.core::str::Bytes"* %0 to i8*
  call void @llvm.lifetime.start(i64 16, i8* %1)
  %2 = bitcast %str_slice* %arg4 to i8*
  call void @llvm.lifetime.start(i64 16, i8* %2)
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %2, i8* bitcast (%str_slice* @const26 to i8*), i64 16, i32 8, i1 false)
  call void @_ZN3str3str5bytes20h68b1cf722a654e56XOgE(%"2.core::str::Bytes"* noalias nocapture sret dereferenceable(16) %0, %str_slice* noalias nocapture dereferenceable(16) %arg4)
  call void @llvm.lifetime.end(i64 16, i8* %2)
  call void @llvm.lifetime.end(i64 16, i8* %1) #3, !alias.scope !0, !noalias !3
  call void @llvm.lifetime.end(i64 16, i8* %1)
  ret void
}

In C/C++ I can use memcpy, strcpy etc...

在 Rust 中使用它们也没有问题:

extern { fn memcpy(dst: *mut libc::c_void, src: *const libc::c_void, len: libc::size_t); }

let t_slice: &mut [c_char] = &mut arr;
unsafe {
    memcpy(t_slice.as_mut_ptr() as *mut libc::c_void, 
        s_ptr as *const libc::c_void, 
        c_s.as_bytes_with_nul().len() as libc::size_t);
}

但最好使用 ptr 模块中的等价物 std::ptr::copy_nonoverlapping:

let t_slice: &mut [c_char] = &mut arr;
unsafe {
    ptr::copy_nonoverlapping(t_slice.as_mut_ptr(), s_ptr, c_s.as_bytes_with_nul().len());
}

但是您应该注意 unsafe 个块,因此您有责任检查 arr 中是否有足够的 space。

这是一种不需要不安全代码的解决方案,但不幸的是,大多数方法都被标记为不稳定。

#![feature(libc)]
#![feature(core)]
#![feature(collections)]

extern crate libc;

use libc::c_char;
use std::ffi::CString;
use std::slice::IntSliceExt;

type arr_type = [c_char; 20];

fn main() {
    let mut c_string: arr_type = [0; 20]; 
    let value = CString::new("happy123").unwrap();

    c_string.clone_from_slice(value.as_bytes_with_nul().as_signed());
}