Skip to content

Commit 0f3d1b8

Browse files
committed
Add as_ptr and as_mut_ptr inherent method to String
Before, they went through `&str` and `&mut str`, which created intermediary references, shrinking provenance to only the initialized parts. `Vec<T>` already has such inherent methods added in rust-lang#61114.
1 parent b97bfc3 commit 0f3d1b8

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

library/alloc/src/string.rs

+75
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,81 @@ impl String {
16121612
&mut self.vec
16131613
}
16141614

1615+
/// Returns a raw pointer to the string's buffer.
1616+
///
1617+
/// The caller must ensure that the string outlives the pointer this
1618+
/// function returns, or else it will end up pointing to garbage.
1619+
/// Modifying the string may cause its buffer to be reallocated,
1620+
/// which would also make any pointers to it invalid.
1621+
///
1622+
/// The caller must also ensure that the memory the pointer (non-transitively) points to
1623+
/// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer
1624+
/// derived from it. If you need to mutate the contents of the slice, use [`as_mut_ptr`].
1625+
///
1626+
/// # Examples
1627+
///
1628+
/// ```
1629+
/// let x = "012".to_string();
1630+
/// let x_ptr = x.as_ptr();
1631+
///
1632+
/// unsafe {
1633+
/// for i in 0..x.len() {
1634+
/// assert_eq!(*x_ptr.add(i), b'0' + u8::try_from(i).unwrap());
1635+
/// }
1636+
/// }
1637+
/// ```
1638+
///
1639+
/// [`as_mut_ptr`]: String::as_mut_ptr
1640+
#[stable(feature = "string_as_ptr", since = "1.63.0")]
1641+
#[inline]
1642+
pub fn as_ptr(&self) -> *const u8 {
1643+
// We shadow the str method of the same name to avoid going through
1644+
// `deref`, which creates an intermediate reference.
1645+
self.vec.as_ptr()
1646+
}
1647+
1648+
/// Returns an unsafe mutable pointer to the string's buffer.
1649+
///
1650+
/// The caller must ensure that the string outlives the pointer this
1651+
/// function returns, or else it will end up pointing to garbage.
1652+
/// Modifying the string may cause its buffer to be reallocated,
1653+
/// which would also make any pointers to it invalid.
1654+
///
1655+
/// The caller must also guarantee to not write invalid UTF-8 to the
1656+
/// initialized part of the buffer.
1657+
///
1658+
/// # Examples
1659+
///
1660+
/// ```
1661+
/// # use core::mem::ManuallyDrop;
1662+
///
1663+
/// // Allocate String big enough for 4 elements.
1664+
/// let size = 4;
1665+
/// // use a ManuallyDrop to avoid a double free
1666+
/// let mut x = ManuallyDrop::new(String::with_capacity(size));
1667+
/// let x_ptr = x.as_mut_ptr();
1668+
///
1669+
/// // Initialize elements via raw pointer writes.
1670+
/// unsafe {
1671+
/// for i in 0..size {
1672+
/// *x_ptr.add(i) = b'A';
1673+
/// }
1674+
/// }
1675+
///
1676+
/// // Create a new String from the ptr
1677+
/// unsafe {
1678+
/// let y = String::from_utf8_unchecked(Vec::from_raw_parts(x_ptr, size, size));
1679+
/// assert_eq!(y.as_str(), "AAAA");
1680+
/// }
1681+
/// ```
1682+
#[stable(feature = "string_as_ptr", since = "1.63.0")]
1683+
#[inline]
1684+
pub fn as_mut_ptr(&mut self) -> *mut u8 {
1685+
// We shadow the str method of the same name to avoid going through
1686+
// `deref_mut`, which creates an intermediate reference.
1687+
self.vec.as_mut_ptr()
1688+
}
1689+
16151690
/// Returns the length of this `String`, in bytes, not [`char`]s or
16161691
/// graphemes. In other words, it might not be what a human considers the
16171692
/// length of the string.

library/alloc/tests/string.rs

+19
Original file line numberDiff line numberDiff line change
@@ -874,3 +874,22 @@ fn test_str_concat() {
874874
let s: String = format!("{a}{b}");
875875
assert_eq!(s.as_bytes()[9], 'd' as u8);
876876
}
877+
878+
#[test]
879+
fn test_string_as_mut_ptr_roundtrip() {
880+
// Allocate String big enough for 4 elements.
881+
let cap = 4;
882+
// use a ManuallyDrop to avoid a double free
883+
let mut x = std::mem::ManuallyDrop::new(String::with_capacity(cap));
884+
let x_ptr = x.as_mut_ptr();
885+
886+
// Create a new String from the ptr
887+
// Because as_mut_ptr goes through Vec::as_mut_ptr, it has provenance for the entire allocation
888+
let mut y = unsafe { String::from_utf8_unchecked(Vec::from_raw_parts(x_ptr, 0, cap)) };
889+
// We have provenance to write to the empty capacity
890+
y.push_str("uwu");
891+
assert_eq!(y.as_str(), "uwu");
892+
893+
y.push('!');
894+
assert_eq!(y.as_str(), "uwu!");
895+
}

0 commit comments

Comments
 (0)