kaisawind's blog
  • 关于
  • 所有帖子

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

  • remacs/remacs#1393

最佳实践

  1. 使用标准类型:优先使用 libc::c_char 或 std::os::raw::c_char
  2. 避免硬编码:不要直接使用 i8 或 u8
  3. 跨平台测试:在不同架构上测试代码
  4. 类型安全:使用 Rust 的类型系统确保安全
  5. 内存管理:注意 C 字符串的内存管理

常见平台类型映射

Rust 类型C 类型x86_64aarch64
libc::c_charchari8u8
libc::c_shortshorti16i16
libc::c_intinti32i32
libc::c_longlongi64i64

注意事项

  1. 平台差异:注意不同平台的类型定义差异
  2. 内存安全:使用 unsafe 代码时确保内存安全
  3. 字符串终止:C 字符串必须以 null 结尾
  4. 编码问题:注意字符编码(UTF-8 vs ASCII)


辽ICP备2021007608号 | © 2026 | kaisawind

Facebook Twitter GitHub