rust: replace `CStr` with `core::ffi::CStr`

`kernel::ffi::CStr` was introduced in commit d126d23801 ("rust: str:
add `CStr` type") in November 2022 as an upstreaming of earlier work
that was done in May 2021[0]. That earlier work, having predated the
inclusion of `CStr` in `core`, largely duplicated the implementation of
`std::ffi::CStr`.

`std::ffi::CStr` was moved to `core::ffi::CStr` in Rust 1.64 in
September 2022. Hence replace `kernel::str::CStr` with `core::ffi::CStr`
to reduce our custom code footprint, and retain needed custom
functionality through an extension trait.

Add `CStr` to `ffi` and the kernel prelude.

Link: faa3cbcca0 [0]
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Tamir Duberstein <tamird@gmail.com>
Link: https://patch.msgid.link/20251018-cstr-core-v18-16-9378a54385f8@gmail.com
[ Removed assert that would now depend on the Rust version. - Miguel ]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
Tamir Duberstein 2025-10-18 15:16:37 -04:00 committed by Miguel Ojeda
parent c5cf01ba8d
commit 3b83f5d5e7
13 changed files with 127 additions and 330 deletions

View File

@ -61,7 +61,7 @@ pub(crate) fn debug_print(&self, prefix: &str, m: &SeqFile) {
mod strings {
use core::str::from_utf8_unchecked;
use kernel::str::CStr;
use kernel::str::{CStr, CStrExt as _};
extern "C" {
static binder_command_strings: [*const u8; super::BC_COUNT];

View File

@ -46,3 +46,5 @@ macro_rules! alias {
}
pub use core::ffi::c_void;
pub use core::ffi::CStr;

View File

@ -3,7 +3,7 @@
use crate::debugfs::file_ops::FileOps;
use crate::ffi::c_void;
use crate::str::CStr;
use crate::str::{CStr, CStrExt as _};
use crate::sync::Arc;
use core::marker::PhantomData;

View File

@ -13,6 +13,7 @@
#[cfg(CONFIG_PRINTK)]
use crate::c_str;
use crate::str::CStrExt as _;
pub mod property;

View File

@ -156,7 +156,9 @@ macro_rules! declare_drm_ioctls {
Some($cmd)
},
flags: $flags,
name: $crate::c_str!(::core::stringify!($cmd)).as_char_ptr(),
name: $crate::str::as_char_ptr_in_const_context(
$crate::c_str!(::core::stringify!($cmd)),
),
}
),*];
ioctls

View File

@ -182,6 +182,8 @@ pub fn name(&self) -> Option<&'static CStr> {
if ptr.is_null() {
None
} else {
use crate::str::CStrExt as _;
// SAFETY: The string returned by `errname` is static and `NUL`-terminated.
Some(unsafe { CStr::from_char_ptr(ptr) })
}

View File

@ -4,7 +4,14 @@
//!
//! C header: [`include/linux/firmware.h`](srctree/include/linux/firmware.h)
use crate::{bindings, device::Device, error::Error, error::Result, ffi, str::CStr};
use crate::{
bindings,
device::Device,
error::Error,
error::Result,
ffi,
str::{CStr, CStrExt as _},
};
use core::ptr::NonNull;
/// # Invariants

View File

@ -19,7 +19,7 @@
pub use ::ffi::{
c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong,
c_ushort, c_void,
c_ushort, c_void, CStr,
};
pub use crate::alloc::{flags::*, Box, KBox, KVBox, KVVec, KVec, VBox, VVec, Vec};
@ -43,7 +43,7 @@
pub use super::error::{code::*, Error, Result};
pub use super::{str::CStr, ThisModule};
pub use super::{str::CStrExt as _, ThisModule};
pub use super::init::InPlaceInit;

View File

@ -4,7 +4,7 @@
//!
//! C header: [`include/linux/seq_file.h`](srctree/include/linux/seq_file.h)
use crate::{bindings, c_str, fmt, types::NotThreadSafe, types::Opaque};
use crate::{bindings, c_str, fmt, str::CStrExt as _, types::NotThreadSafe, types::Opaque};
/// A utility for generating the contents of a seq file.
#[repr(transparent)]

View File

@ -10,9 +10,11 @@
};
use core::{
marker::PhantomData,
ops::{self, Deref, DerefMut, Index},
ops::{Deref, DerefMut, Index},
};
pub use crate::prelude::CStr;
/// Byte string without UTF-8 validity guarantee.
#[repr(transparent)]
pub struct BStr([u8]);
@ -186,58 +188,17 @@ macro_rules! b_str {
// - error[E0379]: functions in trait impls cannot be declared const
#[inline]
pub const fn as_char_ptr_in_const_context(c_str: &CStr) -> *const c_char {
c_str.0.as_ptr()
c_str.as_ptr().cast()
}
/// Possible errors when using conversion functions in [`CStr`].
#[derive(Debug, Clone, Copy)]
pub enum CStrConvertError {
/// Supplied bytes contain an interior `NUL`.
InteriorNul,
mod private {
pub trait Sealed {}
/// Supplied bytes are not terminated by `NUL`.
NotNulTerminated,
impl Sealed for super::CStr {}
}
impl From<CStrConvertError> for Error {
#[inline]
fn from(_: CStrConvertError) -> Error {
EINVAL
}
}
/// A string that is guaranteed to have exactly one `NUL` byte, which is at the
/// end.
///
/// Used for interoperability with kernel APIs that take C strings.
#[repr(transparent)]
pub struct CStr([u8]);
impl CStr {
/// Returns the length of this string excluding `NUL`.
#[inline]
pub const fn len(&self) -> usize {
self.len_with_nul() - 1
}
/// Returns the length of this string with `NUL`.
#[inline]
pub const fn len_with_nul(&self) -> usize {
if self.0.is_empty() {
// SAFETY: This is one of the invariant of `CStr`.
// We add a `unreachable_unchecked` here to hint the optimizer that
// the value returned from this function is non-zero.
unsafe { core::hint::unreachable_unchecked() };
}
self.0.len()
}
/// Returns `true` if the string only includes `NUL`.
#[inline]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
/// Extensions to [`CStr`].
pub trait CStrExt: private::Sealed {
/// Wraps a raw C string pointer.
///
/// # Safety
@ -245,54 +206,9 @@ pub const fn is_empty(&self) -> bool {
/// `ptr` must be a valid pointer to a `NUL`-terminated C string, and it must
/// last at least `'a`. When `CStr` is alive, the memory pointed by `ptr`
/// must not be mutated.
#[inline]
pub unsafe fn from_char_ptr<'a>(ptr: *const c_char) -> &'a Self {
// SAFETY: The safety precondition guarantees `ptr` is a valid pointer
// to a `NUL`-terminated C string.
let len = unsafe { bindings::strlen(ptr) } + 1;
// SAFETY: Lifetime guaranteed by the safety precondition.
let bytes = unsafe { core::slice::from_raw_parts(ptr.cast(), len) };
// SAFETY: As `len` is returned by `strlen`, `bytes` does not contain interior `NUL`.
// As we have added 1 to `len`, the last byte is known to be `NUL`.
unsafe { Self::from_bytes_with_nul_unchecked(bytes) }
}
/// Creates a [`CStr`] from a `[u8]`.
///
/// The provided slice must be `NUL`-terminated, does not contain any
/// interior `NUL` bytes.
pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError> {
if bytes.is_empty() {
return Err(CStrConvertError::NotNulTerminated);
}
if bytes[bytes.len() - 1] != 0 {
return Err(CStrConvertError::NotNulTerminated);
}
let mut i = 0;
// `i + 1 < bytes.len()` allows LLVM to optimize away bounds checking,
// while it couldn't optimize away bounds checks for `i < bytes.len() - 1`.
while i + 1 < bytes.len() {
if bytes[i] == 0 {
return Err(CStrConvertError::InteriorNul);
}
i += 1;
}
// SAFETY: We just checked that all properties hold.
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
}
/// Creates a [`CStr`] from a `[u8]` without performing any additional
/// checks.
///
/// # Safety
///
/// `bytes` *must* end with a `NUL` byte, and should only have a single
/// `NUL` byte (or the string will be truncated).
#[inline]
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
// SAFETY: Properties of `bytes` guaranteed by the safety precondition.
unsafe { core::mem::transmute(bytes) }
}
// This function exists to paper over the fact that `CStr::from_ptr` takes a `*const
// core::ffi::c_char` rather than a `*const crate::ffi::c_char`.
unsafe fn from_char_ptr<'a>(ptr: *const c_char) -> &'a Self;
/// Creates a mutable [`CStr`] from a `[u8]` without performing any
/// additional checks.
@ -301,99 +217,16 @@ pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError
///
/// `bytes` *must* end with a `NUL` byte, and should only have a single
/// `NUL` byte (or the string will be truncated).
#[inline]
pub unsafe fn from_bytes_with_nul_unchecked_mut(bytes: &mut [u8]) -> &mut CStr {
// SAFETY: Properties of `bytes` guaranteed by the safety precondition.
unsafe { &mut *(core::ptr::from_mut(bytes) as *mut CStr) }
}
unsafe fn from_bytes_with_nul_unchecked_mut(bytes: &mut [u8]) -> &mut Self;
/// Returns a C pointer to the string.
///
/// Using this function in a const context is deprecated in favor of
/// [`as_char_ptr_in_const_context`] in preparation for replacing `CStr` with `core::ffi::CStr`
/// which does not have this method.
#[inline]
pub const fn as_char_ptr(&self) -> *const c_char {
as_char_ptr_in_const_context(self)
}
/// Convert the string to a byte slice without the trailing `NUL` byte.
#[inline]
pub fn to_bytes(&self) -> &[u8] {
&self.0[..self.len()]
}
/// Convert the string to a byte slice without the trailing `NUL` byte.
///
/// This function is deprecated in favor of [`Self::to_bytes`] in preparation for replacing
/// `CStr` with `core::ffi::CStr` which does not have this method.
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.to_bytes()
}
/// Convert the string to a byte slice containing the trailing `NUL` byte.
#[inline]
pub const fn to_bytes_with_nul(&self) -> &[u8] {
&self.0
}
/// Convert the string to a byte slice containing the trailing `NUL` byte.
///
/// This function is deprecated in favor of [`Self::to_bytes_with_nul`] in preparation for
/// replacing `CStr` with `core::ffi::CStr` which does not have this method.
#[inline]
pub const fn as_bytes_with_nul(&self) -> &[u8] {
self.to_bytes_with_nul()
}
/// Yields a [`&str`] slice if the [`CStr`] contains valid UTF-8.
///
/// If the contents of the [`CStr`] are valid UTF-8 data, this
/// function will return the corresponding [`&str`] slice. Otherwise,
/// it will return an error with details of where UTF-8 validation failed.
///
/// # Examples
///
/// ```
/// # use kernel::str::CStr;
/// let cstr = CStr::from_bytes_with_nul(b"foo\0")?;
/// assert_eq!(cstr.to_str(), Ok("foo"));
/// # Ok::<(), kernel::error::Error>(())
/// ```
#[inline]
pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
core::str::from_utf8(self.as_bytes())
}
/// Unsafely convert this [`CStr`] into a [`&str`], without checking for
/// valid UTF-8.
///
/// # Safety
///
/// The contents must be valid UTF-8.
///
/// # Examples
///
/// ```
/// # use kernel::c_str;
/// # use kernel::str::CStr;
/// let bar = c_str!("ツ");
/// // SAFETY: String literals are guaranteed to be valid UTF-8
/// // by the Rust compiler.
/// assert_eq!(unsafe { bar.as_str_unchecked() }, "ツ");
/// ```
#[inline]
pub unsafe fn as_str_unchecked(&self) -> &str {
// SAFETY: TODO.
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
}
// This function exists to paper over the fact that `CStr::as_ptr` returns a `*const
// core::ffi::c_char` rather than a `*const crate::ffi::c_char`.
fn as_char_ptr(&self) -> *const c_char;
/// Convert this [`CStr`] into a [`CString`] by allocating memory and
/// copying over the string data.
pub fn to_cstring(&self) -> Result<CString, AllocError> {
CString::try_from(self)
}
fn to_cstring(&self) -> Result<CString, AllocError>;
/// Converts this [`CStr`] to its ASCII lower case equivalent in-place.
///
@ -404,11 +237,7 @@ pub fn to_cstring(&self) -> Result<CString, AllocError> {
/// [`to_ascii_lowercase()`].
///
/// [`to_ascii_lowercase()`]: #method.to_ascii_lowercase
pub fn make_ascii_lowercase(&mut self) {
// INVARIANT: This doesn't introduce or remove NUL bytes in the C
// string.
self.0.make_ascii_lowercase();
}
fn make_ascii_lowercase(&mut self);
/// Converts this [`CStr`] to its ASCII upper case equivalent in-place.
///
@ -419,11 +248,7 @@ pub fn make_ascii_lowercase(&mut self) {
/// [`to_ascii_uppercase()`].
///
/// [`to_ascii_uppercase()`]: #method.to_ascii_uppercase
pub fn make_ascii_uppercase(&mut self) {
// INVARIANT: This doesn't introduce or remove NUL bytes in the C
// string.
self.0.make_ascii_uppercase();
}
fn make_ascii_uppercase(&mut self);
/// Returns a copy of this [`CString`] where each character is mapped to its
/// ASCII lower case equivalent.
@ -434,13 +259,7 @@ pub fn make_ascii_uppercase(&mut self) {
/// To lowercase the value in-place, use [`make_ascii_lowercase`].
///
/// [`make_ascii_lowercase`]: str::make_ascii_lowercase
pub fn to_ascii_lowercase(&self) -> Result<CString, AllocError> {
let mut s = self.to_cstring()?;
s.make_ascii_lowercase();
Ok(s)
}
fn to_ascii_lowercase(&self) -> Result<CString, AllocError>;
/// Returns a copy of this [`CString`] where each character is mapped to its
/// ASCII upper case equivalent.
@ -451,13 +270,7 @@ pub fn to_ascii_lowercase(&self) -> Result<CString, AllocError> {
/// To uppercase the value in-place, use [`make_ascii_uppercase`].
///
/// [`make_ascii_uppercase`]: str::make_ascii_uppercase
pub fn to_ascii_uppercase(&self) -> Result<CString, AllocError> {
let mut s = self.to_cstring()?;
s.make_ascii_uppercase();
Ok(s)
}
fn to_ascii_uppercase(&self) -> Result<CString, AllocError>;
}
impl fmt::Display for CStr {
@ -490,98 +303,75 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
}
impl fmt::Debug for CStr {
/// Formats printable ASCII characters with a double quote on either end, escaping the rest.
///
/// ```
/// # use kernel::c_str;
/// # use kernel::prelude::fmt;
/// # use kernel::str::CStr;
/// # use kernel::str::CString;
/// let penguin = c_str!("🐧");
/// let s = CString::try_from_fmt(fmt!("{penguin:?}"))?;
/// assert_eq!(s.as_bytes_with_nul(), "\"\\xf0\\x9f\\x90\\xa7\"\0".as_bytes());
///
/// // Embedded double quotes are escaped.
/// let ascii = c_str!("so \"cool\"");
/// let s = CString::try_from_fmt(fmt!("{ascii:?}"))?;
/// assert_eq!(s.as_bytes_with_nul(), "\"so \\\"cool\\\"\"\0".as_bytes());
/// # Ok::<(), kernel::error::Error>(())
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\"")?;
for &c in self.as_bytes() {
match c {
// Printable characters.
b'\"' => f.write_str("\\\"")?,
0x20..=0x7e => f.write_char(c as char)?,
_ => write!(f, "\\x{c:02x}")?,
}
}
f.write_str("\"")
/// Converts a mutable C string to a mutable byte slice.
///
/// # Safety
///
/// The caller must ensure that the slice ends in a NUL byte and contains no other NUL bytes before
/// the borrow ends and the underlying [`CStr`] is used.
unsafe fn to_bytes_mut(s: &mut CStr) -> &mut [u8] {
// SAFETY: the cast from `&CStr` to `&[u8]` is safe since `CStr` has the same layout as `&[u8]`
// (this is technically not guaranteed, but we rely on it here). The pointer dereference is
// safe since it comes from a mutable reference which is guaranteed to be valid for writes.
unsafe { &mut *(core::ptr::from_mut(s) as *mut [u8]) }
}
impl CStrExt for CStr {
#[inline]
unsafe fn from_char_ptr<'a>(ptr: *const c_char) -> &'a Self {
// SAFETY: The safety preconditions are the same as for `CStr::from_ptr`.
unsafe { CStr::from_ptr(ptr.cast()) }
}
#[inline]
unsafe fn from_bytes_with_nul_unchecked_mut(bytes: &mut [u8]) -> &mut Self {
// SAFETY: the cast from `&[u8]` to `&CStr` is safe since the properties of `bytes` are
// guaranteed by the safety precondition and `CStr` has the same layout as `&[u8]` (this is
// technically not guaranteed, but we rely on it here). The pointer dereference is safe
// since it comes from a mutable reference which is guaranteed to be valid for writes.
unsafe { &mut *(core::ptr::from_mut(bytes) as *mut CStr) }
}
#[inline]
fn as_char_ptr(&self) -> *const c_char {
self.as_ptr().cast()
}
fn to_cstring(&self) -> Result<CString, AllocError> {
CString::try_from(self)
}
fn make_ascii_lowercase(&mut self) {
// SAFETY: This doesn't introduce or remove NUL bytes in the C string.
unsafe { to_bytes_mut(self) }.make_ascii_lowercase();
}
fn make_ascii_uppercase(&mut self) {
// SAFETY: This doesn't introduce or remove NUL bytes in the C string.
unsafe { to_bytes_mut(self) }.make_ascii_uppercase();
}
fn to_ascii_lowercase(&self) -> Result<CString, AllocError> {
let mut s = self.to_cstring()?;
s.make_ascii_lowercase();
Ok(s)
}
fn to_ascii_uppercase(&self) -> Result<CString, AllocError> {
let mut s = self.to_cstring()?;
s.make_ascii_uppercase();
Ok(s)
}
}
impl AsRef<BStr> for CStr {
#[inline]
fn as_ref(&self) -> &BStr {
BStr::from_bytes(self.as_bytes())
}
}
impl Deref for CStr {
type Target = BStr;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl Index<ops::RangeFrom<usize>> for CStr {
type Output = CStr;
#[inline]
fn index(&self, index: ops::RangeFrom<usize>) -> &Self::Output {
// Delegate bounds checking to slice.
// Assign to _ to mute clippy's unnecessary operation warning.
let _ = &self.as_bytes()[index.start..];
// SAFETY: We just checked the bounds.
unsafe { Self::from_bytes_with_nul_unchecked(&self.0[index.start..]) }
}
}
impl Index<ops::RangeFull> for CStr {
type Output = CStr;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &Self::Output {
self
}
}
mod private {
use core::ops;
// Marker trait for index types that can be forward to `BStr`.
pub trait CStrIndex {}
impl CStrIndex for usize {}
impl CStrIndex for ops::Range<usize> {}
impl CStrIndex for ops::RangeInclusive<usize> {}
impl CStrIndex for ops::RangeToInclusive<usize> {}
}
impl<Idx> Index<Idx> for CStr
where
Idx: private::CStrIndex,
BStr: Index<Idx>,
{
type Output = <BStr as Index<Idx>>::Output;
#[inline]
fn index(&self, index: Idx) -> &Self::Output {
&self.as_ref()[index]
BStr::from_bytes(self.to_bytes())
}
}
@ -612,6 +402,13 @@ macro_rules! c_str {
mod tests {
use super::*;
impl From<core::ffi::FromBytesWithNulError> for Error {
#[inline]
fn from(_: core::ffi::FromBytesWithNulError) -> Error {
EINVAL
}
}
macro_rules! format {
($($f:tt)*) => ({
CString::try_from_fmt(fmt!($($f)*))?.to_str()?
@ -634,40 +431,28 @@ macro_rules! format {
#[test]
fn test_cstr_to_str() -> Result {
let good_bytes = b"\xf0\x9f\xa6\x80\0";
let checked_cstr = CStr::from_bytes_with_nul(good_bytes)?;
let checked_str = checked_cstr.to_str()?;
let cstr = c"\xf0\x9f\xa6\x80";
let checked_str = cstr.to_str()?;
assert_eq!(checked_str, "🦀");
Ok(())
}
#[test]
fn test_cstr_to_str_invalid_utf8() -> Result {
let bad_bytes = b"\xc3\x28\0";
let checked_cstr = CStr::from_bytes_with_nul(bad_bytes)?;
assert!(checked_cstr.to_str().is_err());
Ok(())
}
#[test]
fn test_cstr_as_str_unchecked() -> Result {
let good_bytes = b"\xf0\x9f\x90\xA7\0";
let checked_cstr = CStr::from_bytes_with_nul(good_bytes)?;
// SAFETY: The contents come from a string literal which contains valid UTF-8.
let unchecked_str = unsafe { checked_cstr.as_str_unchecked() };
assert_eq!(unchecked_str, "🐧");
let cstr = c"\xc3\x28";
assert!(cstr.to_str().is_err());
Ok(())
}
#[test]
fn test_cstr_display() -> Result {
let hello_world = CStr::from_bytes_with_nul(b"hello, world!\0")?;
let hello_world = c"hello, world!";
assert_eq!(format!("{hello_world}"), "hello, world!");
let non_printables = CStr::from_bytes_with_nul(b"\x01\x09\x0a\0")?;
let non_printables = c"\x01\x09\x0a";
assert_eq!(format!("{non_printables}"), "\\x01\\x09\\x0a");
let non_ascii = CStr::from_bytes_with_nul(b"d\xe9j\xe0 vu\0")?;
let non_ascii = c"d\xe9j\xe0 vu";
assert_eq!(format!("{non_ascii}"), "d\\xe9j\\xe0 vu");
let good_bytes = CStr::from_bytes_with_nul(b"\xf0\x9f\xa6\x80\0")?;
let good_bytes = c"\xf0\x9f\xa6\x80";
assert_eq!(format!("{good_bytes}"), "\\xf0\\x9f\\xa6\\x80");
Ok(())
}
@ -686,14 +471,12 @@ fn test_cstr_display_all_bytes() -> Result {
#[test]
fn test_cstr_debug() -> Result {
let hello_world = CStr::from_bytes_with_nul(b"hello, world!\0")?;
let hello_world = c"hello, world!";
assert_eq!(format!("{hello_world:?}"), "\"hello, world!\"");
let non_printables = CStr::from_bytes_with_nul(b"\x01\x09\x0a\0")?;
assert_eq!(format!("{non_printables:?}"), "\"\\x01\\x09\\x0a\"");
let non_ascii = CStr::from_bytes_with_nul(b"d\xe9j\xe0 vu\0")?;
let non_printables = c"\x01\x09\x0a";
assert_eq!(format!("{non_printables:?}"), "\"\\x01\\t\\n\"");
let non_ascii = c"d\xe9j\xe0 vu";
assert_eq!(format!("{non_ascii:?}"), "\"d\\xe9j\\xe0 vu\"");
let good_bytes = CStr::from_bytes_with_nul(b"\xf0\x9f\xa6\x80\0")?;
assert_eq!(format!("{good_bytes:?}"), "\"\\xf0\\x9f\\xa6\\x80\"");
Ok(())
}

View File

@ -8,7 +8,7 @@
use super::{lock::Backend, lock::Guard, LockClassKey};
use crate::{
ffi::{c_int, c_long},
str::CStr,
str::{CStr, CStrExt as _},
task::{
MAX_SCHEDULE_TIMEOUT, TASK_FREEZABLE, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE,
},

View File

@ -7,7 +7,7 @@
use super::LockClassKey;
use crate::{
str::CStr,
str::{CStr, CStrExt as _},
types::{NotThreadSafe, Opaque, ScopeGuard},
};
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin};

View File

@ -5,7 +5,7 @@
//! Support for defining statics containing locks.
use crate::{
str::CStr,
str::{CStr, CStrExt as _},
sync::lock::{Backend, Guard, Lock},
sync::{LockClassKey, LockedBy},
types::Opaque,