资讯动态

Rust 标准库 `core::ffi::c_ushort` 详解:与 C 的 `unsigned short` 精确对接的 FFI 类型

发布时间:2026/9/10 3:53:30 来源:尧图企业网站定制
Rust 标准库core::ffi::c_ushort详解与 C 的unsigned short精确对接的 FFI 类型【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rustc_ushort是 Rust 标准库core::ffi模块提供的一组与 C 语言基础类型一一对应的类型之一它专门用于表示 C 语言中的unsigned short。凡是涉及通过 FFI 调用 C 函数、解析 C 结构体尤其是网络套接字、Windows 系统数据结构等平台相关布局的场景都离不开它。读完本文你将掌握c_ushort的定义、实现原理、与原生u16的差异、在 Rust 标准库源码中的真实应用以及如何在你的 FFI 代码中正确使用它。一、c_ushort是什么c_ushort在语义上等价于 C 语言的unsigned short类型。根据 library/core/src/ffi/c_ushort.md 中的官方定义Equivalent to Csunsigned shorttype.也就是说它不是一个新发明的整数类型而是 C 的unsigned short在 Rust 世界中的官方代言人。当你需要声明一个与 C 侧unsigned short字段、参数或返回值对齐的 Rust 类型时就应当使用c_ushort而不是直接写u16。为什么不能直接使用u16这是 FFI 类型体系的核心设计动机。C 标准对整数类型只做最小宽度约束而不承诺具体宽度。对于unsigned shortC 标准只要求它是一个与short同尺寸的无符号整数而short至少要能容纳 16 位。在绝大多数主流平台上这个类型恰好就是 16 位因此c_ushort几乎总是等于u16但在一些小众/非主流esoteric系统上它可能与u16不同。引用 library/core/src/ffi/c_ushort.md 的原文This type will almost always be [u16], but may differ on some esoteric systems. The C standard technically only requires that this type be an unsigned integer with the same size as a [short].翻译过来即该类型几乎总是u16但在某些特殊系统上可能不同C 标准仅要求它是一个与short同尺寸的无符号整数。这正是 FFI 类型存在的意义——把平台相关的宽度决策交给标准库而不是让每个开发者自行猜测。二、源码级实现c_ushort在标准库中如何定义c_ushort的定义位于 library/core/src/ffi/primitives.rstype_alias! { c_ushort.md, c_ushort u16; }这行代码通过一个type_alias!宏展开为完整的类型定义。该宏定义在 library/core/src/ffi/primitives.rsmacro_rules! type_alias { { $Docfile:tt, $Alias:ident $Real:ty; $( $Cfg:tt )* } { #[doc include_str!($Docfile)] $( $Cfg )* #[stable(feature core_ffi_c, since 1.64.0)] pub type $Alias $Real; } }几个关键点值得展开文档即源码宏通过#[doc include_str!($Docfile)]把对应的 Markdown 文档也就是 c_ushort.md直接嵌入到类型上作为 rustdoc 文档。这就是为什么c_ushort的类型文档与 Markdown 文件内容完全一致。稳定版本#[stable(feature core_ffi_c, since 1.64.0)]表明整套core_ffi_c类型含c_ushort自Rust 1.64.0起稳定可用无需#![feature(...)]即可在稳定版 Rust 中直接使用。本质是类型别名c_ushort是一个pub type别名。在绝大多数目标平台上它直接别名到u16与u16在布局size/alignment、位宽、运算行为上完全一致可以相互赋值、相互转换。同族类型一览c_ushort不是孤立的它是 library/core/src/ffi/primitives.rs 中一整组 C 整数/浮点 FFI 类型的一员Rust FFI 类型对应 C 类型定义c_scharsigned chari8c_ucharunsigned charu8c_shortsigned shorti16c_ushortunsigned shortu16c_intint按目标架构多数平台i32avr/msp430 为i16c_uintunsigned int按目标架构c_longlong64 位非 Windows 平台为i64否则i32c_ulongunsigned long对应c_long的无符号版本c_longlonglong longi64c_ulonglongunsigned long longu64c_charchar按目标平台有符号性选择i8/u8可以注意到c_short与c_ushort是这一族中少数直接硬编码为固定宽度i16/u16的类型之一而c_int、c_long、c_char、c_double都通过 primitives.rs 中的cfg_select!按目标平台条件选择例如c_long在 64 位非 Windows 平台是i64在 Windows 与 32 位平台是i32c_char在 aarch64/arm/riscv 等架构默认无符号在 Windows 与 Apple 平台默认有符号。这再次印证unsigned short在 C 标准中的约束非常宽松Rust 选择几乎总是u16是贴合绝大多数真实 ABI 的务实决策。导出路径与兼容模块c_ushort在 library/core/src/ffi/mod.rs 中被公开导出mod primitives; #[stable(feature core_ffi_c, since 1.64.0)] pub use self::primitives::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort, };因此你可以通过以下任一方式引入use core::ffi::c_ushort; // no_std 环境 use std::ffi::c_ushort; // std 环境std::ffi 重导出 core::ffi此外历史遗留的std::os::raw模块也提供了相同的类型std::os::raw::c_ushort其实现通过alias_core_ffi!宏直接把core::ffi的类型和文档一并重导出见 library/std/src/os/raw/mod.rsmacro_rules! alias_core_ffi { ($($t:ident)*) {$( #[stable(feature raw_os, since 1.1.0)] #[doc include_str!(concat!(../../../../core/src/ffi/, stringify!($t), .md))] #[doc(cfg(all()))] pub type $t core::ffi::$t; )*} } alias_core_ffi! { c_char c_schar c_uchar c_short c_ushort // ... }注意 library/std/src/os/raw/mod.rs 的模块注释明确建议Compatibility module for C platform-specific types. Use [core::ffi] instead.兼容模块请改用core::ffi。新代码应优先使用core::ffi::c_ushortstd::os::raw仅用于兼容旧代码。三、标准库内部的真实用法c_ushort在哪里发挥作用看标准库自身如何使用c_ushort是理解它价值的最佳途径。它集中出现在平台相关的网络与系统数据结构中。1. Windows 套接字端口字段sin_port/sin6_port在 library/std/src/sys/net/connection/socket/windows.rs 中sockaddr_in与sockaddr_in6的端口字段直接用c_ushort声明pub sin_port: c_ushort, // sockaddr_in 的端口对应 C 的 unsigned short pub sin6_port: c_ushort, // sockaddr_in6 的端口这与 Windows 平台 C 头文件winsock2.h/ws2def.h中的定义一致——sin_port/sin6_port在 Windows 上正是unsigned short。如果这里贸然写成u16在当前平台没有问题但一旦移植到某个unsigned short宽度不同的平台就会破坏 ABI这正是标准库坚持使用c_ushort的原因。2.linger结构Cygwin 平台在 library/std/src/sys/net/connection/socket/unix.rs 中Cygwin 目标平台的set_linger实现把libc::linger的字段按c_ushort处理并用c_ushort::MAX做溢出钳制#[cfg(target_os cygwin)] pub fn set_linger(self, linger: OptionDuration) - io::Result() { let linger libc::linger { l_onoff: linger.is_some() as libc::c_ushort, l_linger: cmp::min(linger.unwrap_or_default().as_secs(), libc::c_ushort::MAX as u64) as libc::c_ushort, }; unsafe { setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) } }这里有一个非常实用的技巧c_ushort::MAX可以被直接当作常量使用因为它就是u16拥有u16的全部关联常量与方法。标准库用它把Duration秒数钳制到该类型可表示的最大值避免溢出。3. Windows 重解析点Reparse Point数据结构在 library/std/src/sys/pal/windows/c.rs 中Windows 的REPARSE_DATA_BUFFER相关结构体大量使用c_ushort字段pub ReparseDataLength: c_ushort, pub Reserved: c_ushort, pub SubstituteNameOffset: c_ushort, pub SubstituteNameLength: c_ushort, pub PrintNameOffset: c_ushort, pub PrintNameLength: c_ushort,这些字段在 Windows 系统调用如 symlink/挂载点查询中都是 16 位无符号整数Rust 标准库用c_ushort精确复刻了 C 侧的布局。可以看到c_ushort在标准库中的使用场景高度一致凡是与 C ABI 直接接触的整数字段端口、标志、长度偏移量都用c_ushort而非裸u16来表达。四、c_ushort与u16的异同维度u16c_ushort本质Rust 原生无符号整数Cunsigned short的 FFI 类型别名当前实现原生类型在绝大多数目标平台 u16平台保证固定 16 位C 标准仅要求与short同尺寸最小 16 位特殊平台可能不同可调用方法全部u16方法全部u16方法因为是同一类型使用场景通用整数运算声明 C 结构体字段、extern 函数签名、平台数据结构稳定性语言内置自 Rust 1.64.0 稳定core_ffi_c关键结论当前所有受支持目标平台上c_ushort与u16就是同一个类型可以互相赋值、互相比较、混用方法。它的价值不在现在不同而在万一不同时不至于出错——把平台差异的责任交给标准库你的 FFI 代码只需表达意图这是一个 C 的unsigned short。五、如何在你的 FFI 代码中使用c_ushort1. 声明 C 结构体假设 C 侧有这样一个结构体struct ioctl_req { unsigned short cmd; /* 命令码 */ unsigned short flags; /* 标志位 */ unsigned long arg; /* 参数 */ };对应的 Rust 声明应为use core::ffi::{c_ulong, c_ushort}; #[repr(C)] struct IoctlReq { cmd: c_ushort, flags: c_ushort, arg: c_ulong, }2. 声明外部函数签名use core::ffi::c_ushort; extern C { // C: unsigned short get_checksum(const unsigned char *data, unsigned short len); fn get_checksum(data: *const u8, len: c_ushort) - c_ushort; }3. 与 libc crate 协作在依赖libccrate 的项目中libc::c_ushort与core::ffi::c_ushort是同一类型。标准库的测试直接验证了这一点——library/std/src/os/raw/tests.rs 用TypeId断言所有 C 类型在libc与std::os::raw之间完全一致macro_rules! ok { ($($t:ident)*) {$( assert!(TypeId::of::libc::$t() TypeId::of::raw::$t(), {} is wrong, stringify!($t)); )*} } #[test] fn same() { use crate::os::raw; ok!(c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong c_float c_double); }这意味着你在extern C声明里用core::ffi::c_ushort在与libc结构体对接时无需任何转换二者天然同构。4. 与c_short的配套使用signed short对应的c_short定义为i16与c_ushort是有符号/无符号配对关系详见 library/core/src/ffi/c_short.md。注意c_short的约束比c_ushort更宽松C 标准只要求short是至少 16 位的有符号整数某些系统甚至可能把short定义为i32。因此在跨平台 FFI 代码中务必始终使用c_short/c_ushort这对类型而不要假设它们就是i16/u16。5. 使用注意事项尽量用于边界只在extern C函数签名、#[repr(C)]结构体字段等 ABI 边界使用c_ushort在 Rust 内部的纯逻辑计算中直接用u16即可二者可无缝互转。避免无符号陷阱C 的unsigned short在表达式中会被整型提升为int有符号而 Rust 的c_ushort不会发生这种隐式提升。编写涉及运算的 FFI 逻辑时注意两语言语义差异。读取平台结构体时保持原样如标准库处理linger、REPARSE_DATA_BUFFER那样从系统层读回的结构体字段保持c_ushort类型只在需要运算时显式as转换如val.l_linger as u64见 library/std/src/sys/net/connection/socket/unix.rs。六、总结core::ffi::c_ushort是 Rust 为 C 的unsigned short提供的标准 FFI 类型在绝大多数目标平台上它就是u16定义见 primitives.rs自 Rust 1.64.0 起稳定它的存在把C 标准对unsigned short只有最小宽度约束这一不确定性封装起来让 FFI 代码只表达语义、不赌平台Rust 标准库自身在 Windows 套接字端口windows.rs、Cygwinlingerunix.rs、重解析点结构体c.rs等真实 ABI 边界都使用它是最可靠的参照用例新代码请使用core::ffi::c_ushort或std::ffi::c_ushort并可将std::os::raw视为仅供旧代码兼容的别名模块见 std::os::raw 模块说明。下次再写extern C或#[repr(C)]结构体时遇到 C 的unsigned short字段就用c_ushort吧——这正是标准库为你准备好的正确答案。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价