Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add as_ptr and as_mut_ptr inherent method to String #97483

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions library/alloc/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,81 @@ impl String {
&mut self.vec
}

/// Returns a raw pointer to the string's buffer.
///
/// The caller must ensure that the string outlives the pointer this
/// function returns, or else it will end up pointing to garbage.
/// Modifying the string may cause its buffer to be reallocated,
/// which would also make any pointers to it invalid.
///
/// The caller must also ensure that the memory the pointer (non-transitively) points to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The caller must also ensure that the memory the pointer (non-transitively) points to
/// The caller must also ensure that the memory the pointer points to

We know it points to u8 so we don't need the transitivity comment.

/// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer
/// is never written to using this pointer or any pointer

There is no UnsafeCell here.

/// derived from it. If you need to mutate the contents of the slice, use [`as_mut_ptr`].
///
/// # Examples
///
/// ```
/// let x = "012".to_string();
/// let x_ptr = x.as_ptr();
///
/// unsafe {
/// for i in 0..x.len() {
/// assert_eq!(*x_ptr.add(i), b'0' + u8::try_from(i).unwrap());
/// }
/// }
/// ```
///
/// [`as_mut_ptr`]: String::as_mut_ptr
#[stable(feature = "string_as_ptr", since = "1.63.0")]
#[inline]
pub fn as_ptr(&self) -> *const u8 {
// We shadow the str method of the same name to avoid going through
// `deref`, which creates an intermediate reference.
self.vec.as_ptr()
}

/// Returns an unsafe mutable pointer to the string's buffer.
///
/// The caller must ensure that the string outlives the pointer this
/// function returns, or else it will end up pointing to garbage.
/// Modifying the string may cause its buffer to be reallocated,
/// which would also make any pointers to it invalid.
///
/// The caller must also guarantee to not write invalid UTF-8 to the
/// initialized part of the buffer.
///
/// # Examples
///
/// ```
/// # use core::mem::ManuallyDrop;
///
/// // Allocate String big enough for 4 elements.
/// let size = 4;
/// // use a ManuallyDrop to avoid a double free
/// let mut x = ManuallyDrop::new(String::with_capacity(size));
/// let x_ptr = x.as_mut_ptr();
///
/// // Initialize elements via raw pointer writes.
/// unsafe {
/// for i in 0..size {
/// *x_ptr.add(i) = b'A';
/// }
/// }
///
/// // Create a new String from the ptr
/// unsafe {
/// let y = String::from_utf8_unchecked(Vec::from_raw_parts(x_ptr, size, size));
/// assert_eq!(y.as_str(), "AAAA");
/// }
/// ```
#[stable(feature = "string_as_ptr", since = "1.63.0")]
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
// We shadow the str method of the same name to avoid going through
// `deref_mut`, which creates an intermediate reference.
self.vec.as_mut_ptr()
}

/// Returns the length of this `String`, in bytes, not [`char`]s or
/// graphemes. In other words, it might not be what a human considers the
/// length of the string.
Expand Down
19 changes: 19 additions & 0 deletions library/alloc/tests/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -874,3 +874,22 @@ fn test_str_concat() {
let s: String = format!("{a}{b}");
assert_eq!(s.as_bytes()[9], 'd' as u8);
}

#[test]
fn test_string_as_mut_ptr_roundtrip() {
// Allocate String big enough for 4 elements.
let cap = 4;
// use a ManuallyDrop to avoid a double free
let mut x = std::mem::ManuallyDrop::new(String::with_capacity(cap));
let x_ptr = x.as_mut_ptr();

// Create a new String from the ptr
// Because as_mut_ptr goes through Vec::as_mut_ptr, it has provenance for the entire allocation
let mut y = unsafe { String::from_utf8_unchecked(Vec::from_raw_parts(x_ptr, 0, cap)) };
// We have provenance to write to the empty capacity
y.push_str("uwu");
assert_eq!(y.as_str(), "uwu");

y.push('!');
assert_eq!(y.as_str(), "uwu!");
}