qidao123.com技术社区-IT企服评测·应用市场
标题:
rust学习二十.2、不安全代码之不安全函数、特质和FFI
[打印本页]
作者:
梦见你的名字
时间:
2025-3-12 16:47
标题:
rust学习二十.2、不安全代码之不安全函数、特质和FFI
本文涉及到不安全函数和FFI(foreign function interface)(外部函数接口)
一、简述
在开始前,先介绍下unsafe代码块。
这个其实上一个章节有效到,但是未有正式介绍。
unsafe块形如:
unsafe{
}
unsafe块可以位于一个函数/方法内,也可以位于函数参数中,比方:
println!("r1: {}, r2: {}", unsafe { *r1 }, unsafe { *r2 });
1.1、不安全函数和特质
不安全函数
标志为unsafe的函数,只必要在函数前添加unsafe关键字即可,不安全函数的两个要点:
不安全函数内部的代码可以不要再写unsafe了
调用不安全函数的代码块依然必要些unsafe{}代码块
相关书籍建议我们尽量把不安全的操作封装在不安全函数中,起到一个黑盒作用,更容易管理。
不安全特质(trait)
此外,特质也可以是不安全的,比方:
unsafe trait Foo {
// methods go here
}
unsafe impl Foo for i32 {
// method implementations go here
}
fn main() {}
复制代码
原书提到一个术语:invariant。原书把它翻译为不变式。
当特质中至少有一个方法包罗了编译器无法验证的invariant,特质是不安全,必要把特质标注为unsafe。
这里根据本人的明白,我认为更加公道妥当的如下:
当特质中至少有一个方法包罗了编译器无法验证的不变量/不变约束,特质是不安全,必要把特质标注为unsafe。
所谓的不变量/不变约束,可以这么明白(来自文心一言):
某个变量永远不会被访问为 null。
某个数据结构始终保持有效状态(如二叉搜索树的有序性)。
指针只指向有效的内存区域。
复制代码
这些有助于我们明白不安全特质。
但我们是不是可以简要明白:不安全特质,就是部门方法利用了不安全代码?
1.2、外部函数接口
Foreign Function interface,简写为ffi,中文含义是外部函数接口。
很多编程语言都有外部函数接口,比方java可以通过JNI调用C++的DLL,Python可以利用ctypes调用C++的DLL。
RUST在底层是怎样实现的,不必要太过关心,反正脱离不了一些固定的窠臼,总之就是可以和C语言交互
FFI的存在能过满足以下几个要求:
1.利用现有的一些资源,不至于浪费
2.利用更加擅长的语言编写一些代码,以便提升效率,大概达成特定目的
至于rust怎样对接c,c++,python,java等,不是本文重点,此处略。
二、例子
本例包罗两个主要内容:调用C标准函数、定义和执行一个不安全函数
use std::ffi::CString;
use encoding_rs::GBK;
use libc::{c_char, c_int};
// 在 Rust 中声明 C 的 abs 函数
extern "C" {
fn abs(x: i32) -> i32;
}
extern "C" {
// 定义C语言的printf函数
//#[link(name = "c")]
fn printf(format: *const c_char, ...)-> c_int;
}
fn main() {
//1.0 直接调用 C 的 abs 函数,无需任何前置条件
call_c_abs();
//1.1 调用C语言函数,需要用到C语言环境
call_c_function(); // 此处注释掉,因为需要用到C语言环境
//2.0 不安全函数例子
//不安全函数例子-被标记为unsafe的函数,可以执行不安全的操作,但必须放在unsafe块中执行
unsafe {dangerous()};
}
fn call_c_abs(){
let number = -42;
let absolute_value = unsafe { abs(number) };
println!("调用C的abs:{}的绝对值= {}", number, absolute_value);
}
fn call_c_function() {
// 创建一个C字符串
let format = CString::new("%s %d\n").unwrap();
// 1. 原始UTF-8字符串
let utf8_message = "调用C的printf:C语言,我来了!这是发自一个rust的程序的消息";
// 2. 将UTF-8转换为GBK
let (gbk_bytes, _, _) = GBK.encode(utf8_message);
// 3. 创建一个GBK编码的CString
// 注意:需要去掉末尾的null字节,因为CString会自动添加
let gbk_bytes_without_null = if gbk_bytes.ends_with(&[0]) {
&gbk_bytes[..gbk_bytes.len() - 1]
} else {
&gbk_bytes[..]
};
let message = CString::new(gbk_bytes_without_null).unwrap();
let number = 114975;
// 调用C语言的printf函数
unsafe {
//这样打印会乱码,如何不乱码了?
printf(format.as_ptr(), message.as_ptr(), number);
}
}
//不安全函数例子
//至于内容是不是安全的,我们不关心。重点是,这个函数是被标记为unsafe的
unsafe fn dangerous() {
println!("我是一个不安全的函数,因此不需要unsafe块就可以执行其它不安全的操作。");
play();
}
unsafe fn play() {
//注意,本函数基本上和另外一个地方的例子一模一样,但是此处居然不触发致命错误,为什么?
// 如果game的值是英文,那么会有很大概率触发异常,但是如果game的内容是中文,则不会触发致命错误。
// 为什么?
let game = Box::new(String::from("fuck dog")); //这个有很大概况触发致命错误
//let game = Box::new(String::from("跑步、捉迷藏、踢足球、下棋")); //这个不会错误,为什么?
let raw_game = Box::into_raw(game);
println!("游戏内容: {:?}", *raw_game);
drop(Box::from_raw(raw_game));
println!("游戏内容(来自Box指针): {:?}", *raw_game);
}
复制代码
执行结果之一:
如果把play()方法的game替换为:
let game = Box::new(String::from("跑步、捉迷藏、踢足球、下棋"));
则有很大概率不触发致命错误:
这个颇让人迷惑,是rust的潜伏bug吗?
三、小结
rust的不安全函数,总体是一个好东西,一个方面提供了封装,别的容易定位,最后可以少写unsafe块
外部函数接口(ffi)则使得rust可以和其它语言对接
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/)
Powered by Discuz! X3.4