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
Closed

RFC: truly unsized types #813

pnkfelix opened this issue Feb 5, 2015 · 17 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@pnkfelix
Copy link
Member

pnkfelix 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 RFCs that have been postponed and may be revisited at a later time. label Feb 5, 2015
@mzabaluev
Copy link
Contributor

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

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

@mzabaluev
Copy link
Contributor

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
Copy link
Contributor

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

@retep998
Copy link
Member

retep998 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
Copy link
Contributor

@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
Copy link
Contributor

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
Copy link

@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
Copy link
Contributor

@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
Copy link

@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
Copy link

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
Copy link
Contributor

@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
Copy link
Contributor

@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
Copy link
Contributor

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
Copy link
Contributor

This came up in #996 as well.

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 21, 2016
@DemiMarie
Copy link

@SSheldon @nikomatsakis Ping?

@SSheldon
Copy link

@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
Copy link
Contributor

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

@petrochenkov petrochenkov removed the postponed RFCs that have been postponed and may be revisited at a later time. label Feb 24, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants