Rust 中 char 在不同 CPU 架构的类型错误 - Thu, Oct 15, 2020
使用 Rust 的 bindgen 链接 C 语言库时,不同平台中 c_char 存储类型不同,导致编译错误。本文介绍问题的原因和解决方法。
使用 Rust 的 bindgen 链接 C 语言库时,不同平台中 c_char 存储类型不同,导致编译错误。本文介绍问题的原因和解决方法。
提示: Rust 每 6 周发布一个新版本,建议使用
rustup保持最新。
概述
在使用 Rust 的 bindgen 链接 C 语言库时,不同平台的 c_char 存储类型不同,导致编译错误:
- ARM64 (aarch64):
c_char使用u8进行存储 - AMD64 (x86_64):
c_char使用i8进行存储
错误示例
error[E0308]: mismatched types
--> src/callproc.rs:62:31
|
62 | unsafe { build_string(NULL_DEVICE.as_ptr() as *const i8) }.into()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u8, found i8
|
= note: expected type `*const u8`
found type `*const i8`
error[E0308]: mismatched types
--> src/callproc.rs:69:13
|
69 | encoded_file.const_data_ptr() as *const i8,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u8, found i8
|
= note: expected type `*const u8`
found type `*const i8`
error[E0308]: mismatched types
--> src/callproc.rs:78:17
|
78 | "Opening process input file".as_ptr() as *const i8,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u8, found i8
|
= note: expected type `*const u8`
found type `*const i8`
问题原因
不同架构的 C 语言 char 类型定义不同:
// x86_64
typedef signed char c_char; // i8
// aarch64
typedef unsigned char c_char; // u8
直接使用 *const i8 或 *const u8 会导致跨平台编译失败。
解决方法
方法一:使用 libc::c_char(推荐)
使用 libc crate 提供的 c_char 类型,它会根据平台自动选择正确的类型:
use libc::c_char;
// 错误写法
unsafe { build_string(NULL_DEVICE.as_ptr() as *const i8) }.into()
// 正确写法
unsafe { build_string(NULL_DEVICE.as_ptr() as *const c_char) }.into()
方法二:使用 std::os::raw::c_char
标准库也提供了 c_char 类型:
use std::os::raw::c_char;
// 正确写法
unsafe { build_string(NULL_DEVICE.as_ptr() as *const c_char) }.into()
方法三:条件编译
如果需要更细粒度的控制,可以使用条件编译:
#[cfg(target_arch = "x86_64")]
type MyChar = i8;
#[cfg(target_arch = "aarch64")]
type MyChar = u8;
// 使用自定义类型
unsafe { build_string(NULL_DEVICE.as_ptr() as *const MyChar) }.into()
使用示例
基本用法
use libc::{c_char, c_int};
extern "C" {
fn c_function(s: *const c_char) -> c_int;
}
fn main() {
let s = "Hello, world!";
let ptr = s.as_ptr() as *const c_char;
unsafe {
c_function(ptr);
}
}
字符串转换
use libc::c_char;
use std::ffi::{CString, CStr};
// Rust 字符串转 C 字符串
fn rust_to_c_string(s: &str) -> *mut c_char {
CString::new(s).unwrap().into_raw()
}
// C 字符串转 Rust 字符串
unsafe fn c_to_rust_string(s: *const c_char) -> String {
CStr::from_ptr(s).to_string_lossy().into_owned()
}
// 记得释放 C 字符串内存
unsafe fn free_c_string(s: *mut c_char) {
drop(CString::from_raw(s));
}
Cargo.toml 配置
添加 libc 依赖:
[dependencies]
libc = "0.2"
相关 Issue
最佳实践
- 使用标准类型:优先使用
libc::c_char或std::os::raw::c_char - 避免硬编码:不要直接使用
i8或u8 - 跨平台测试:在不同架构上测试代码
- 类型安全:使用 Rust 的类型系统确保安全
- 内存管理:注意 C 字符串的内存管理
常见平台类型映射
| Rust 类型 | C 类型 | x86_64 | aarch64 |
|---|---|---|---|
libc::c_char | char | i8 | u8 |
libc::c_short | short | i16 | i16 |
libc::c_int | int | i32 | i32 |
libc::c_long | long | i64 | i64 |
注意事项
- 平台差异:注意不同平台的类型定义差异
- 内存安全:使用 unsafe 代码时确保内存安全
- 字符串终止:C 字符串必须以 null 结尾
- 编码问题:注意字符编码(UTF-8 vs ASCII)