4

I have write some FFI code to 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();

How can I update arr with the String s? In C/C++ I can use memcpy, strcpy etc...

I have tried many, like use rlibc::memcpy and found it can't be used with libc.. , but the compiler don't let me pass, there is very few info about array in Rust.


Add: After read the replys, I want to add some info and more question.

1.

In C++, I use strcpy_s to copy string to char array, cause the length of string and the size of array are all known.

I have tried both methods below.

the std::iter::Zip method, seems very like strcpy_s, but I don't know if there is some performance affect.

the copy_nonoverlapping method, it use as_mut_ptr() cast the array to pointer then there is no length info, since it is in unsafe { } block, and I have tried copy a string longer then the array and there is no error displays... I wonder if that's ok?

And is there a function in Rust like strcpy_s in C++?

2.

I'm using windows and msvc, for the char array, I mean not deal with encoding or use default codepage encoding.

below are all ok in source file:

C++:

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

Qt:

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

Python 3:

s = 'world is 世界'

But in Rust, below may be wrong? as I see in Eclipse Debug window.

let s = "world is 世界";

I found rust-encoding and tried these:

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

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

Is there any better way to do in Rust?

1
  • "After read the replys, I want to add some info and more question." - adding more info is OK, but it's Stack Overflow etiquette to ask different questions separately. Besides, people may not even see your second question. Commented Mar 30, 2015 at 21:25

3 Answers 3

4

Here's one solution that doesn't require unsafe code, but unfortunately most of the methods are marked unstable.

#![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());
}
Sign up to request clarification or add additional context in comments.

Comments

2

I suggest updating every character on its own by iterating over the array and the string simultaneously, and assigning the string characters to the array characters. I added the final \0 to the Rust-string.

#![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\0";
    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


in most cases llvm will optimize that loop to a 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
}

6 Comments

"you probably don't want to pass [non-ASCII] on to C" — why not? I've written and used C code that handles UTF-8 just fine.
then you should not be working with c_char but with uint8_t or similar
Even if the C method in question takes in a char *?
then it is not supporting utf8 or the interface specification is wrong
Could you provide some resources that corroborate the claim that C methods that use char * only accept ASCII and that if you intend to use UTF-8 you should use an 8-bit integral type?
|
1

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

There is no problem to use them in Rust also:

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);
}

But it's better to use rust equivalent std::ptr::copy_nonoverlapping from ptr module:

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());
}

You should note unsafe blocks however, so it's your responsibility to check if there is enough space in arr.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.