Skip to content

into/from_parts - #97

Open
Baptistemontan wants to merge 4 commits into
mozilla:mainfrom
Baptistemontan:into_from_raw
Open

Baptistemontan wants to merge 4 commits into
mozilla:mainfrom
Baptistemontan:into_from_raw

Conversation

@Baptistemontan

Copy link
Copy Markdown
Contributor

Add into_parts and from_parts for ThinVec, mirroring Vec API, but with different expectation for from_parts, which are closer to the ones of Rc::from_raw, since with the header into_parts is doing something akin to Rc::into_raw with the pointer offset.

close #55

@Baptistemontan Baptistemontan changed the title into/from_part into/from_parts Sep 16, 2026

@emilio emilio left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What's the use case for this?

Comment thread src/lib.rs
///
/// [`from_parts`]: ThinVec::from_parts
#[must_use = "losing the pointer will leak memory"]
pub fn into_parts(self) -> (NonNull<T>, usize, usize) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't get the point of returning pointer / len / capacity separately.

A from_raw / into_raw would be reasonable I guess, but that doesn't work for ZSTs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

well capacity is indeed optionnal in theory and be retrieved in the allocation, but length is required for ZSTs, we could have a non-zst variant that just gives a pointer. But without the gecko-ffi feature, how do we properly check if it is a singleton or not ? we might get back a pointer that does'nt have a header, and without the capacity we can't know without doubts if there is one or not, heck even with the cap we can't, but since it has no capacity it did'nt allocate so we can just return a newly created one, and length does'nt helps as it could have allocated then cleared. So we do need the trio for reconstruction.
But at the end of the day, it's the same as Vec, from_parts do have stronger expectations than the Vec counterpart but the idea is the same: you get back a pointer, a length and a capacity, and you now own the underlying buffer and are responsible for it's memory management.
As for a use case, I guess the same ones as Vec::into_parts, you can create a ThinVec, operate on it, then need a way to own the allocation because there is an API that you use that needs it for XYZ reasons, might be for FFI where it expect the pointer of the buffer, but you can't keep the ThinVec alive as now it might alias, or keeping it alive for the whole usage might be difficult.

@emilio emilio left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it'd be better to have a:

fn into_raw(self) -> NonNull<T> { .. }
fn from_raw(raw: NonNull<T>) -> Self { .. }

Where NonNull<T> would be the fake len thingie, and for the others it'd be the offsetted pointer into the buffer.

That seems like it'd fulfill the same use cases in practice?

@Baptistemontan

Copy link
Copy Markdown
Contributor Author

Where NonNull<T> would be the fake len thingie

Seams awfully footgunny, pretty bad idea to return a seamingly ptr to T but with "unless it's a ZST, then it's the len, and you can't deref it unless align::<T> == 1", I guess we could have the fake pointer always aligned so it's always valid for deref, so ptr = (len + 1) * align_of::<T>, this would make max capacity based on T tho. But now how do you check for singletons ? there are cases where it just returns a dangling pointer with no header, how do you check for that ? Do we just return ptr = align_of::<T> when there is no allocation ? now we would trivially know if there is allocation by simply checking ptr == align (there is an absurd case where a type has an align so big it manage to produce an adress the allocator might return, but this would just produce a memory leak in the even more absurd case where the allocator actually return that specific adress for that specific type)
Also, in this case do we make raw accessors available? like unsafe fn len_from_raw(ptr: NonNull<T>) -> usize ?
I get that the idea is that because everything is in the allocation we should be able to just return a ptr and keep the "one ptr wide", but since we return a NonNull<T> instead of a NonNull<Header> there is informations that are lost, and we need strong invariants to derive the header ptr from the T pointer.
I'm all for ideas on how to make that work, but I guess we can have into_parts and if we find a reliable way to make into_raw we can add it later ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add from_raw method

2 participants