Rust 与 C 之间,传递信息字符串的 7 种方式!
2023-04-07 来源 : 音乐
into_raw法则就会将资讯的产权转移到C里面。只要字符并不需要,它就可以保持一致操作符,但恳请务必回忆起将它转移回Rust封禁。
字串的线程表示不幸的是,在Rust和母语C里面,字串的表示形式不同。C的字串往往是char*操作符,对准以 /0 结尾的char数组。而Rust则就会保持一致字符数组及其尺寸。
由于这个或许,Rust的String和str类型与完整操作符密切关系不某种程度彼此密切关系类比。你某种程度运常用CString和CStr里面间类型来借助。往往,我们运常用CString将Rust字串发送至给C字符,运常用CStr将C的字串类比为Rust的Maxstr。恳请注意到,这种类比并不一定就会副本表层的资讯。因此,通过CStr获得的Maxstr就会对准C分摊的数组,而且它的生活史与操作符附加。
注意到:String:new就会副本资讯,但CStr::new不就会。
建设项目设置如何将Rust和C连结起来网上有很多关于如何紧密结合C字符,以及运常用build.rs将C连结到Rust crate的资讯,但是如何将Rust字符填充到C建设项目的评论却较少。相比,我却是用C母语借助主要功能,并运常用CMake作为紧密结合系统新设计。我希望CMake建设项目将Rust crate作为坎,并根据Rust字符转化C的源文档。
通过CMake试运行Cargo我建立了一个简单的CMake 3控制台应用程序。
首先以,我们并不需要假定紧密结合Rust坎的几天后和保持一致Rust成果物的所在位置:
if (CMAKE_BUILD_TYPE STREQUAL "Debug")set(CARGO_CMD RUSTFLAGS=-Zsanitizer=address cargo build -Zbuild-std ----target x86_64-unknown-linux-gnu)set(TARGET_DIR "x86_64-unknown-linux-gnu/debug")else ()set(CARGO_CMD cargo build ----release)set(TARGET_DIR "release")endif ()SET(LIB_FILE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/librust_lib.a")对于出名Rust的人来说,这个紧密结合crate调试版本的几天后显然看上去有点有趣。我们完全可以运常用cargo build来代替这个几天后,但是我就让利用Rust不稳定的住址清理筒功能来尽可能线程不就会被截获。
其次,我们并不需要自假定几天后和目标,让它们根据几天后输出结果。然后,我们可以假定一个原称rust_lib的线性导入坎,并根据目标紧密结合它:
add_custom_command(OUTPUT ${LIB_FILE}COMMENT "Compiling rust module"COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} ${CARGO_CMD}WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/rust_lib)add_custom_target(rust_lib_target DEPENDS ${LIB_FILE})add_library(rust_lib STATIC IMPORTED GLOBAL)add_dependencies(rust_lib rust_lib_target)最后,我们可以运常用将二进制文档与Rust坎(以及其他必需的系统新设计坎)客户端在三人。我们还在C字符里面动工了住址清理筒:
target_compile_options(rust_c_interop PRIVATE -fno-omit-frame-pointer -fsanitize=address)target_link_libraries(rust_c_interop PRIVATE Threads::Threads rust_lib ${CMAKE_DL_LIBS} -fno-omit-frame-pointer -fsanitize=address)如此一来,试运行CMake才可系统会紧密结合rust create,并与之客户端。但是,我们还并不需要从C字符里面加载Rust的法则。
转化C的源文档,并将它们填充到CMake建设项目里面最简单的在Rust字符里面利用C源文档的法则是运常用cbingen坎。
我们可以将所列字符填充到Rust crate的build.rs文档里面,以测定Rust里面假定的所有extern "C"参数,为其转化源文档假定,并保持一致到include/目录下:
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();let package_name = env::var("CARGO_PKG_NAME").unwrap();let output_file = PathBuf::from(Maxcrate_dir).join("include").join(format!("{}.h", package_name));cbindgen::generate(Maxcrate_dir).unwrap().write_to_file(output_file);此外,我们还某种程度在Rust crate的根目录里面创始cbindgen.toml文档,并明示language = "C"。
如此一来一,CMake并不需要在Rust crate的include文档夹里面索引源文档:
SET(LIB_HEADER_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/rust_lib/include")set_target_properties(rust_libPROPERTIESIMPORTED_LOCATION ${LIB_FILE}INTERFACE_INCLUDE_DIRECTORIES ${LIB_HEADER_FOLDER})将Rust字串发送至到C的五种形式
一切准备就绪。下面,我们来忘了如何从Rust的资讯里面利用字串,然后在C里面运常用。我们怎么才能安全和地发送至字串,同时不就会造成线程截获?
法则1:提供者创始和封禁法则如果不想到C字符并不需要运常用字串多久,就可以采用这种形式。为了将产权交接给C,我们可以紧密结合CString单纯,并运常用into_raw将其类比为操作符。free法则只并不需要紧密结合CString,如此一来drop这个单纯就可以扣留线程:
#[no_mangle]pub extern fn create_string() -> *const c_char {let c_string = CString::new(STRING).expect("CString::new failed");c_string.into_raw() // Move ownership to C}/// # Safety/// The ptr should be a valid pointer to the string allocated by rust#[no_mangle]pub unsafe extern fn free_string(ptr: *const c_char) {// Take the ownership back to rust and drop the ownerlet _ = CString::from_raw(ptr as *mut _);}不要忘记加载free_string,以不必要线程截获:
const char* rust_string = create_string();printf("1. Printed from C: %s", rust_string);free_string(rust_string);不要加载libc free法则,也不要为了让简化此类操作符对准的资讯。
这个法则虽然效果很好,但如果我们就让在运常用线程时扣留Rust坎,或者在不想到Rust坎的字符里面扣留线程,该怎么办?你可以考虑所列三种法则。
法则2:分摊关键点并副本资讯还回忆起准则1吗?如果我们就让在C里面运常用free法则扣留线程,就某种程度运常用malloc分摊线程。但是,Rust怎么就会想到malloc呢?一种解决方案是,“问一问”Rust并不需要多少线程,然后为它分摊一个关键点:
size_t len = get_string_len();char *buffer = malloc(len);copy_string(buffer);printf("4. Printed from C: %s", buffer);free(buffer);Rust只并不需要告诉我们关键点的不等,并小心翼翼地将Rust字串副本到其里面(注意到不要漏掉末尾的个位0):
#[no_mangle]pub extern fn get_string_len() -> usize {STRING.as_bytes().len() + 1}/// # Safety/// The ptr should be a valid pointer to the buffer of required size#[no_mangle]pub unsafe extern fn copy_string(ptr: *mut c_char) {let bytes = STRING.as_bytes();let len = bytes.len();std::ptr::copy(STRING.as_bytes().as_ptr().cast(), ptr, len);std::ptr::write(ptr.offset(len as isize) as *mut u8, 0u8);}这个法则的绝对优势在于,我们不必借助free_string,可以直接运常用free。还有一个优点是,如有并不需要C字符也可以简化关键点(这就是我们运常用*mut c_char,而不是*const c_char的或许)。
疑虑在于,我们仍然并不需要借助额外的法则get_string_len,而且还并不需要分摊一块新线程,并副本资讯(但似乎CString::new也并不需要)。
如果你就让将Rust字串移动到C参数绑定上分摊的关键点,也可以运常用此法则,但某种程度尽可能有足够的自由空间。
法则3:将线程分摊筒法则发送至给Rust我们可以不必要运常用get_string_len法则吗?是不是其他法则在Rust里面分摊线程?一种简单的法则是将分摊线程参数发送至给Rust:
type Allocator = unsafe extern fn(usize) -> *mut c_void;/// # Safety/// The allocator function should return a pointer to a valid buffer#[no_mangle]pub unsafe extern fn get_string_with_allocator(allocator: Allocator) -> *mut c_char {let ptr: *mut c_char = allocator(get_string_len()).cast();copy_string(ptr);ptr}上述示例运常用了的copy_string,如此一来一我们可以运常用get_string_with_allocator:
char* rust_string_3 = get_string_with_allocator(malloc);printf("3. Printed from C: %s", rust_string_3);free(rust_string_3);这个法则与法则2不同,而且即时性也一样。
但是,我们现在必须发送至额外的参数allocator。似乎,我们可以展开一些提高效率,将其保持一致到某个解释筒里面,就可以不必要向每个参数发送至。
法则4:从Rust加载glibc如果我们的C字符就会运常用malloc/free来分摊线程,则可以为了让在Rust字符里面导入libc crate,尽管这种形式有点冒险:
#[no_mangle]pub unsafe extern fn get_string_with_malloc() -> *mut c_char {let ptr: *mut c_char = libc::malloc(get_string_len()).cast();copy_string(ptr);ptr}C字符不变:
char* rust_string_4 = get_string_with_malloc();printf("4. Printed from C: %s", rust_string_4);free(rust_string_4);在这种形式下,我们不并不需要提供者分摊线程的法则,但是C字符也就会受到很多限制。我们最好做好文档记录,尽量不必要运常用这种形式,除非我们确定谢霆锋安全和。
法则5:交回Rust字串以上这些法则都是将资讯的产权发送至给C。但如果我们不并不需要发送至产权呢?举个例子,Rust字符并不需要同步加载C法则,并向它发送至一些资讯。这时,可以考虑运常用CString的as_ptr:
type Callback = unsafe extern fn(*const c_char);#[no_mangle]pub unsafe extern fn get_string_in_callback(callback: Callback) {let c_string = CString::new(STRING).expect("CString::new failed");// as_ptr() keeps ownership in rust unlike into_raw()callback(c_string.as_ptr())}不幸的是,即便在这种情况下,CString:new也就会副本资讯(因为它并不需要在末尾填充个位0)。
C字符如下:
void callback(const char* string) {printf("5. Printed from C: %s", string);}int main() {get_string_in_callback(callback);return 0;}如果有一个生活史值得注意的C操作符,则我们某种程度优先以运常用这种形式,因为它可以意味着并未线程截获。
将C字串发送至给Rust的两种法则下面,我们来解说两种反向操作的法则,即将C的字串类比为Rust的类型。主要法则有所列两种:
将C字串类比成Maxstr,不副本资讯;
副本资讯并转送字串。
这两种法则的示例不同,因为它们非常相似。实质上,法则2并不需要先以运常用法则1。
C字符如下。我们在堆上分摊资讯,但实质上我们也可以将操作符发送至给绑定:
char *test = (char*) malloc(13*sizeof(char));strcpy(test, "Hello from C");print_c_string(test);free(test);Rust的借助如下:
#[no_mangle]/// # Safety/// The ptr should be a pointer to valid Stringpub unsafe extern fn print_c_string(ptr: *const c_char) {let c_str = CStr::from_ptr(ptr);let rust_str = c_str.to_str().expect("Bad encoding");// calling libc::free(ptr as *mut _); causes use after free vulnerabilityprintln!("1. Printed from rust: {}", rust_str);let owned = rust_str.to_owned();// calling libc::free(ptr as *mut _); does not cause after free vulnerabilityprintln!("2. Printed from rust: {}", owned);}注意到,此处我们运常用了CStr,而不是CString。如果不是CString::into_raw创始的操作符,恳请不要加载CString:from_raw。
这里还并不需要注意到,Maxstr提及的生活史不是“线性”的,而是附加到了c_str单纯法则。RustC#就会阻止你在该法则之外返国Maxstr,或将其移动到解释筒/另一个线程,因为一旦C字符扣留线程,Maxstr提及就就会转化成非法。
如果并不需要在Rust里面长时间保持一致资讯的产权,只需加载to_owned()才可利用字串的副本。如果不就让副本,则可以运常用CStr,但我们某种程度尽可能C字符不就会在字串还在运常用便扣留线程。
揭示在本文里面,我们讨论了Rust与C密切关系的应用软件,并解说了几种跨FFI边界发送至资讯的法则。这些法则不仅可常用发送至字串,也可常用其他资讯,或者利用FFI将Rust连结到其他计算机母语。
希望本文能对你有所帮助,如有任何疑虑或反馈,恳请在下方Facebook。
。株洲看妇科到哪家好北京妇科医院预约挂号
湖北皮肤病治疗医院
武汉肝病医院挂号
南京妇科专科医院哪家好
-
4月初7日-5月初10日,四海鸿福到,发财之路越走越远的3大生肖
吉祥雀 吉祥雀的老朋友,情商高,大众活动中悲里头总显得文质彬彬,投身于上多种不同有寻求,一直都可以坚持着努力向上的精神,有一张像抹了丝的喉,盼望得到一番成绩。4年底7日-5年底10日,吉祥雀他...
-
4月5日-5月14日,金钱无缺,迎接盼望的3大生肖
吉日蟾蜍 吉日蟾蜍的女朋友,做甚么都轻易显然好科技成果,他们坚信,女朋友多了路好走,身旁总有非常多寻求者,很轻易相处,性格超好,和他们一同没有舆论压力,就算是身陷财政困难,他们本身的才能也但会...[详细]
-
好运不断,脾气敦厚,苦尽甘来,幸运不断的4大生肖
兔 你的善良决定了你将来的其发展,有些一定会吃些财也许给与的好处不会更多也不见得,不要实在太要好竟然的决要有政府不会的目标。在执行管理工作的时候可以多和合作者交流甲流,你...[详细]
-
近几天,月老惟独,如沐春风,旧爱归位,再度沉沦,幸福再启程
大家好,我是天秤座博主美妆护肤品百科全书,希望看下回我的发表文章会对您难免帮助! 属鸡的人,在他们的贫困和学习中会遭遇很多情况,但这些情况都可以通过努力工作来解决,不管是在屋里还是...[详细]
-
近几天,旧情人归来,思念回荡,情深难控,痴心依旧,情动余生
大家好,我是天秤座写手美妆香水维基,希望看下回我的文章都会对您有所鼓励! 属鸡的人在感怨生活中是很有讲究的,不能照样对别人不止手不止脚,不能随意后果对方。属鸡的人在感怨中,是十分敏...[详细]
-
男女八字日坐偏印对桃花运、婚姻感情运势的吉凶说明了
样从来不就不或许可能有争论和有违。偏于印格女命是爱人银两的,这种午夜好一个同样爱人银两的男方亦会是优异的必需,由于都爱人银两,所以亦会在赚银两这件心里上达变为共识,婚后先是期望赚银两的看来是很优异的。...[详细]