您当前的位置:首页 >> 潮流饰家

Rust 与 C 之间,发送至字符串的 7 种方式!

2024-01-12 12:17:43

要用于as_ptr。它有一个巨大的占优,因为 C 编码不应被囚这块CPU,而且还就会限制操作符的生活史。但问不让将这个操作符留有到某个在实践中形态当中,或将其引定时给另一个调用,也不应要将这样的操作符作为变量指令行的结果返回去。

into_raw工具就会将图表的产权转移到C当中。只要编码需,它就可以保持一致操作符,但问应忘了将它转移回去Rust写入。

正则表约式的CPU问到

不幸的是,在Rust和母语C当中,正则表约式的问到作法各有不同。C的正则表约式通常是char*操作符,看成以 /0 结尾的char数据类型。而Rust则就会留有字符串数据类型及其间隔。

由于这个理由,Rust的String和str类别与类似操作符中间不应要彼此间匹配。你必要用于CString和CStr当中间类别来框架。通常,我们用于CString将Rust正则表约式引定时给C编码,用于CStr将C的正则表约式匹配为Rust的Andrewstr。问留意,这种匹配非常一定就会解码中上层的图表。因此,通过CStr取得的Andrewstr就会看成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间歇性地址排查内置机制来确保安全CPU不就会被销毁。

其次,我们需自概念指令和目的,让它们根据指令转换内置结果。然后,我们可以概念一个叫作rust_lib的一个系统定时入托,并根据目的框架它:

add_custom_command(OUTPUT ${LIB_FILE} COMMENT"Compiling rust module" COMMANDCARGO_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 PRIVATEThreads::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/书目下:

letcrate_dir = env:: var( "CARGO_MANIFEST_DIR").unwrap; letpackage_name = env:: var( "CARGO_PKG_NAME").unwrap; letoutput_file = PathBuf::from(Andrewcrate_dir) . join( "include") . join(format!( "{}.h", package_name)); cbindgen::generate(Andrewcrate_dir) .unwrap .write_to_file(output_file);

此外,我们还必要在Rust crate的exe当中始创cbindgen.toml份文件,并指明language = "C"。

接下来,CMake需在Rust crate的include份文件系统当中匹配常量:

SET(LIB_HEADER_FOLDER " ${CMAKE_CURRENT_SOURCE_DIR}/rust_lib/include" ) set_target_properties(rust_lib PROPERTIES IMPORTED_LOCATION ${LIB_FILE} INTERFACE_INCLUDE_DIRECTORIES ${LIB_HEADER_FOLDER})

将Rust正则表约式引定时到C的五种作法

一切准备好。前面,我们来是不是如何从Rust的图表当中利用正则表约式,然后在C当中用于。我们怎么才能人身安全地引定时正则表约式,同时不就会致使CPU销毁?

工具1:共享始创和写入工具

如果不其实C编码需用于正则表约式多久,就可以采用这种作法。为了将产权接管给C,我们可以框架CString简单,并用于into_raw将其匹配为操作符。free工具需用框架CString,先drop这个简单就可以被囚CPU:

#[no_mangle] pub extern fn create_string -> * constc_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: * constc_char) { // Take the ownership back to rust and drop the owner let _ = CString::from_raw(ptr as*mut _); }

不让忘记指令行free_string,以消除CPU销毁:

constchar* rust_string = create_string; printf( "1. Printed from C: %s", rust_string); free_string(rust_string);

不让指令行libc free工具,也不让为了让修改此类操作符看成的图表。

这个工具虽然效用良好,但如果我们一心在用于CPU时被囚Rust托,或者在不其实Rust托的编码当中被囚CPU,该怎么办?你可以考量此表三种工具。

工具2:分派缓冲的区并解码图表

还忘了前提1吗?如果我们一心在C当中用于free工具被囚CPU,就必要用于malloc分派CPU。但是,Rust怎么就会其实malloc呢?一种提供商是,“问一问”Rust需多少CPU,然后为它分派一个缓冲的区:

size_tlen = 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 asisize) as*mut u8, 0u8); }

这个工具的占优在于,我们不应框架free_string,可以直接用于free。还有一个优点是,如有需C编码也可以修改缓冲的区(这就是我们用于*mut c_char,而不是*const c_char的理由)。

缺陷在于,我们即便如此需框架额外的工具get_string_len,而且还需分派一块上新CPU,并解码图表(但其实CString::new也需)。

如果你一心将Rust正则表约式伸展到C变量栈上分派的缓冲的区,也可以用于此工具,但必要确保安全有必要的室内空间。

工具3:将CPU分派内置工具引定时给Rust

我们可以消除用于get_string_len工具吗?有没有其他工具在Rust当中分派CPU?一种恰当的工具是将分派CPU变量引定时给Rust:

type Allocator = unsafeexternfn( usize) -> *mut c_void ; ///# Safety ///The allocator function should return a pointer to a valid buffer #[no_mangle] pub unsafeexternfn get_string_with_allocator( allocator: Allocator) -> *mut c_char { letptr: *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来分派CPU,则可以为了让在Rust编码当中转用libc crate,尽管这种作法有一点伙伴们:

#[no_mangle] pubunsafe extern fn get_string_with_malloc -> *mut c_char { letptr: *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);

在这种作法下,我们不需共享分派CPU的工具,但是C编码也就会受到很多限制。我们不错作好应用程序记事,尽量消除用于这种作法,除非我们确认百分百人身安全。

工具5:改作Rust正则表约式

以上这些工具都是将图表的产权引定时给C。但如果我们不需引定时产权呢?举个值得注意,Rust编码需同步指令行C工具,并向它引定时一些图表。这时,可以考量用于CString的as_ptr:

type Callback = unsafe externfn(* constc_char); #[no_mangle] pub unsafe externfn 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编码如下:

voidcallback( constchar* string) { printf( "5. Printed from C: %s", string); } intmain{ get_string_in_callback(callback); return0; }

如果有一个生活史目前为止的C操作符,则我们必要优不须用于这种作法,因为它可以意味着没有CPU销毁。

将C正则表约式引定时给Rust的两种工具

前面,我们来解说两种反向操作者的工具,即将C的正则表约式匹配为Rust的类别。主要工具有此表两种:

将C正则表约式匹配成Andrewstr,不解码图表; 解码图表并接收正则表约式。

这两种工具的请注意相同,因为它们非常相似。实质上,工具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 String pub unsafe extern fn print_c_string(ptr: *const c_char) { letc_str = CStr::from_ptr(ptr); letrust_str = c_str.to_str.expect( "Bad encoding"); // calling libc::free(ptr as *mut _); causes use after free vulnerability println!( "1. Printed from rust: {}", rust_str); letowned = rust_str.to_owned; // calling libc::free(ptr as *mut _); does not cause after free vulnerability println!( "2. Printed from rust: {}", owned); }

留意,此处我们用于了CStr,而不是CString。如果不是CString::into_raw始创的操作符,问不让指令行CString:from_raw。

这里还需留意,Andrewstr提到的生活史不是“一个系统”的,而是启动时到了c_str简单工具。Rustcodice_就会阻扰你在该工具之外返回去Andrewstr,或将其伸展到在实践中变量/另一个调用,因为一旦C编码被囚CPU,Andrewstr提到就就会演变成非法。

如果需在Rust当中但会保持一致图表的产权,需用指令行to_owned只需利用正则表约式的副本。如果不一心解码,则可以用于CStr,但我们必要确保安全C编码不就会在正则表约式还在用于期间被囚CPU。

揭示

在本文当中,我们发表意见了Rust与C中间的数据源者,并解说了几种衔接FFI边界引定时图表的工具。这些工具不仅可用于引定时正则表约式,也可用于其他图表,或者利用FFI将Rust连通到其他编程母语。

期望本文能对你难免试图,如有任何缺陷或反馈,问在顶部留言。

☞ 雷军:“将来 Ultra 母舰将朝向全球发售”;戴尔同年实质上退出俄罗斯;Type 4.8发行|自恋头条

☞微软说明了史上最重的软件:极低约 36 斤的 C/C++ codice_!

☞ 80岁还在写编码!Hello World发明人、UNIX命名者工程项目登上GitHub热榜

痛风怎样止痛
八子补肾胶囊
流感嗓子疼说不出来话吃什么药
广州试管婴儿第三代费用多少
胃老是反酸烧心怎么办
相关阅读
友情链接