Skip to content

Clarify safety invariants in Vec::from_raw_parts #139816

New issue

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

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

Already on GitHub? # to your account

Open
SyxtonPrime opened this issue Apr 14, 2025 · 1 comment
Open

Clarify safety invariants in Vec::from_raw_parts #139816

SyxtonPrime opened this issue Apr 14, 2025 · 1 comment
Labels
A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@SyxtonPrime
Copy link

Location

https://doc.rust-lang.org/std/vec/struct.Vec.html#method.from_raw_parts

Summary

Currently the documentation of Vec::from_raw_parts claims that both:

  • The size of T times the capacity (ie. the allocated size in bytes) needs to be the same size as the pointer was allocated with.
  • capacity needs to be the capacity that the pointer was allocated with.

This is somewhat confusing. The first condition arises from #95016 which explicitly removed the second condition in favor of the first. The second condition was then added back in in #99216 without any specific comment about it being necessary.

Together these conditions imply that T needs to have the same size and alignment as what ptr was allocated with. But the whole point of #95016 is that this isn't actually the case. (If this is actually the case, I think this could be reworded to be more clear)

Broadly what I am trying to understand is if the following 2 bits of code which convert between Vec<[T; N]> and Vec<T> are UB or not:

fn flatten<const N: usize>(vec: Vec<[T; N]>) -> Vec<T> {
   let mut v = mem::ManuallyDrop::new(v);
   
   let len = v.len() * N;
   let cap = v.capacity() * N;
   let p = v.as_mut_ptr() as *mut T;
   unsafe {
      Vec::from_raw_parts(ptr, new_len, new_cap)
   }
}

fn recombine<const N: usize>(vec: Vec<T>) -> Vec<[T; N]> {
   let mut v = mem::ManuallyDrop::new(v);
   
   assert_eq!(v.len() % N == 0);
   assert_eq!(v.capacity() % N == 0);
   
   let len = v.len() / N;
   let cap = v.capacity() / N;
   let p = v.as_mut_ptr() as *mut [T; N];
   unsafe {
      Vec::from_raw_parts(ptr, new_len, new_cap)
   }
}

As far as I can tell these should work (and seem to in the playground) but they rely on the second condition being overly strict and the first condition being the one which matters,

@SyxtonPrime SyxtonPrime added the A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools label Apr 14, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Apr 14, 2025
@lolbinarycat
Copy link
Contributor

related: #119304

@lolbinarycat lolbinarycat added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Apr 14, 2025
@jieyouxu jieyouxu added T-libs Relevant to the library team, which will review and decide on the PR/issue. and removed T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Apr 15, 2025
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants