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

RFC: truly unsized types #813

Closed
pnkfelix opened this issue Feb 5, 2015 · 17 comments

Comments

Projects
None yet
8 participants
@pnkfelix
Copy link
Member

commented Feb 5, 2015

Further subdivide unsized types into dynamically-sized types, implementing
an intrinsic trait DynamicSize, and types of indeterminate size. References
for the latter kind will be thin, while allocating slots or copying values
of such types is not possible outside unsafe code.

Rendered

Tracking issue for postponed PR #709

@pnkfelix pnkfelix added the postponed label Feb 5, 2015

@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 9, 2015

With support from the compiler, unsized types could opt out of Sized:

pub struct CStr {
    head: libc::c_char
}
impl !Sized for CStr { }
@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 9, 2015

The thin-pointer requirements can be expressed with a couple of traits that have blanket impls for all sized types:

pub trait AsPtr<Raw = Self> {
    fn as_ptr(&self) -> *const Raw;
}

pub trait FromPtr<Raw = Self> {
    unsafe fn from_ptr<'a>(ptr: *const Raw) -> &'a Self;
}

impl<T> AsPtr<T> for T {
    fn as_ptr(&self) -> *const T { self as *const T }
}

impl<T> FromPtr<T> for T {
    unsafe fn from_ptr<'a>(ptr: *const T) -> &'a T { std::mem::transmute(&*ptr) }
}

These traits could then be implemented explicitly (or via #[derive(...)]) by !Sized types.

@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 9, 2015

I have updated the branch with the ideas described above. The rewritten proposal is much leaner and cleaner.

@retep998

This comment has been minimized.

Copy link
Member

commented Feb 9, 2015

Given a type like this, how would I rewrite it using your proposal and implement your traits for it?

#[repr(C)] pub struct TOKEN_PRIVILEGES {
    PrivilegeCount: DWORD,
    Privileges: [LUID_AND_ATTRIBUTES; 0], // This should be unsized
}
@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 9, 2015

@retep998 You'd need to opt it out of Sized:

impl !Sized for TokenPrivileges { }

The pointer conversion traits, in your case, can be implemented the same way as with Sized types in the proposal. It makes sense to make the traits available to #[derive], as most implementations would be similarly trivial.

To access individual members of the privileges array, I'd be looking into providing an Index implementation on TokenPrivileges, where I'd check the index/slice bounds against the count field and use offset method on a raw pointer to get the needed offset into the actual array.

(BTW, perhaps changing the case of the names to Rust conventions, like I have done above, would make for better bindings; other than function names, the exact C naming is not relevant to Rust code AFAIK, and functions can have their linkage names overridden with an attribute)

@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 11, 2015

I have added a question about landing the proposed pointer conversion traits earlier as a lead-in to start adapting code for eventual support of !Sized.

@SSheldon

This comment has been minimized.

Copy link

commented Feb 11, 2015

@mzabaluev, your AsPtr trait is possible to implement for DSTs (like AsPtr<str>). You can have pointers to DSTs, they are just fat pointers. For example:

let s = "hello";
let p: *const str = s as *const str;
println!("{:?}", std::mem::size_of_val(&p));

Pointers in rust aren't guaranteed to be a single word; even DSTs can be converted to a raw pointer. Since every reference can be converted to a pointer, I'm not sure how this trait would help, and it doesn't guarantee that the pointers you get from it are a single word.

@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 11, 2015

@SSheldon, this is as intended. A bound for thin pointers can still be expressed, e.g.

fn want_my_pointers_thin<T: ?Sized, U>(v: &T) -> *const U
    where T: AsPtr<U>, U: Sized
{
    v.as_ptr()
}
@SSheldon

This comment has been minimized.

Copy link

commented Feb 12, 2015

@mzabaluev, that want_my_pointers_thin function won't accept CStr for the following definition:

struct CStr {
    head: libc::c_char,
}

impl !Sized for CStr { }

impl AsPtr<CStr> {
    fn as_ptr(&self) -> *const CStr { self as *const CStr }
}

even though CStr pointers would be thin, so it should accept them.

Sure, we could just define AsPtr<c_char> for CStr instead, but that doesn't help for cases with unsized FFI pointers.

@SSheldon

This comment has been minimized.

Copy link

commented Feb 12, 2015

My concern is that AsPtr doesn't allow expressing a bound that accepts any thin pointer; it really only seems useful in the case of CStr, since CStr can be represented as a c_char pointer, and it doesn't help for the case of an unsized FFI type. (For example, if I want to represent an Objective-C object with an unsized NSObject struct, pointers to it will still be *const NSObject, and there's no better sized type I can use for the pointer.)

@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 12, 2015

@SSheldon your example doesn't seem valid; did you mean:

impl AsPtr<CStr> for CStr { ... }

If CStr is unsized, there should not be an impl like that, but rather

impl AsPtr<c_char> for CStr { ... }

You can do this already with DSTs; we've agreed in #592 that CStr will start off as a newtype on [c_char].

@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 12, 2015

@SSheldon

if I want to represent an Objective-C object with an unsized NSObject struct, pointers to it will still be *const NSObject

If NSObject does not have DST contents (like object type pointers/references) and implements !Sized, the pointer should be thin.

@mzabaluev

This comment has been minimized.

Copy link
Contributor

commented Feb 12, 2015

Argh, sorry, my generic fn want_my_pointer_thin uses the Sized bound so it won't work. You can still use AsPtr to concrete types for bounds, but I should think more about the generic usages.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

commented Apr 10, 2015

This came up in #996 as well.

@DemiMarie

This comment has been minimized.

Copy link

commented Feb 21, 2017

@SSheldon

This comment has been minimized.

Copy link

commented Feb 21, 2017

@DemiMarie not sure what you're pinging about :) If you're wondering about the status of this, it's still postponed. #1524 was an RFC opened aimed at solving this, there are some good comments recently from that PR. It was postponed again because at the current time there are higher priority things for rust to tackle and solving this would introduce significant complexity.

@petrochenkov

This comment has been minimized.

Copy link
Contributor

commented Jan 28, 2018

I'm going to close this issue because #1861 was accepted and implemented.
#2255 can be used for discussing all the related complexities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.