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。
。株洲看妇科到哪家好北京妇科医院预约挂号
湖北皮肤病治疗医院
武汉肝病医院挂号
南京妇科专科医院哪家好
上一篇: 广西多措并举助力高校本科生在桂就业
-
“X杀手”踏入大好消息!Threads宣布已在欧盟正式上线
财联社12翌年15日讯(撰稿人 马兰)周四,Meta首席可执行扎克伯格宣布,交友该平台Threads并未在欧盟委员会南部上架。 ...
-
湖人主教练长文回应将被解雇以及与湖人篮球员的不和
1翌年6日,纽约尼克斯对阵灰熊,纽约尼克斯主教练员达文·汉姆在今年给出新了一个很短的答案,回应了最近有关他和篮球员之间存有相去甚远、不和以及他可能被撤换的报道。 1、当被问及最近的...[详细]
-
魏牌摩卡 PHEV(Coffee 01)巴黎车展上市,售价据统计 6 万欧
10 月底 17 日,2022 巴黎车展上,魏牌发布能源双子星比比 PHEV(Coffee 01)、拿炭 PHEV(Coffee 02)两款汽车,并月底底比比 PHEV(Coffee 01)正式在欧洲...[详细]
-
耶伦指示:AI正对金融稳定构成威胁
来自:华尔街见闻英美两国政府越来越注意AI新科技有可能导致的保险业的系统性可能会。12年底14日周四,英美两国财政部长耶伦暗示,英美两国政府机构机构将把人工智能及其有可能构成的威胁列为...[详细]
-
尼克斯狂胜76人!布朗森闪耀30分,恩比德30+10也无可救主
今天我们来聊聊那场让篮球迷们热血沸腾的NBA小牛队。哦,你没听错,就是那场美国纽约兰多夫和新泽西76人的交手。话问道回来,大家都知道篮球赛有时就像烧烤店里的肉串,你忘记不知道下一串会是什么味道。...[详细]
-
出海强入汽车产业强国腹地 比亚迪成为中国新能源产业出口纵观
子产品覆盖了消费类3C电量、动力电量、光储并重化等信息技术,特别是动力电量信息技术,其代表作——刀片电量,成功通过形态创新将磷酸铁电量的比率和安全部都是性最大者程度融合,并通过了大型企业最武断的电量裹...[详细]