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

Creating an unsized struct #18806

Closed
SimonSapin opened this issue Nov 9, 2014 · 8 comments
Closed

Creating an unsized struct #18806

SimonSapin opened this issue Nov 9, 2014 · 8 comments

Comments

@SimonSapin
Copy link
Contributor

There is apparently no way to construct unsized struct. This can be worked around with transmute, but only if the struct only has one field (the unsized one). Even with a single field, there should be a way to do this without unsafe.

Test case:

pub struct Foo {
    bytes: [u8]
}

impl Foo {
    fn new(bytes: &[u8]) -> &Foo {
        &Foo { bytes: *bytes }
    }
}

Output:

a.rs:7:10: 7:31 error: the trait `core::kinds::Sized` is not implemented for the type `Foo`
a.rs:7         &Foo { bytes: *bytes }
                ^~~~~~~~~~~~~~~~~~~~~
a.rs:7:10: 7:31 note: structs must have a statically known size to be initialized
a.rs:7         &Foo { bytes: *bytes }
                ^~~~~~~~~~~~~~~~~~~~~

A work-around:

    fn new(bytes: &[u8]) -> &Foo {
        unsafe { ::std::mem::transmute(bytes) }
    }
@arielb1
Copy link
Contributor

arielb1 commented Nov 9, 2014

Foo { bytes: *bytes } attempts to construct a Foo on the stack and return it, which can't work (as the Foo is variably-sized), so you have to construct a Foo manually, then transmute it from a TraitObject or Slice<T>.

@SimonSapin
Copy link
Contributor Author

Hum, ok. Returning &T for a "newly created" T doesn’t makes sense, so this case kinda has to be a transmute. Maybe the error message can be improved? And if T has more than one field, the values have to be already laid out correctly somewhere.

So let’s look at returning Box<T> with T unsized, as a fat pointer to newly allocated space. The following fails with the same error message as above, and I don’t know how to work around it without making error-prone guesses at the struct memory layout.

pub struct Foo {
    header: u16,
    bytes: [u8]
}

impl Foo {
    fn new(bytes: Box<[u8]>) -> Box<Foo> {
        box Foo { header: 4, bytes: *bytes }
    }
}

@bkoropoff
Copy link
Contributor

You need to use generics and coercions (using as is broken at the moment):

struct Foo<Sized? T> {
    foo: T
}

fn main() {
    let foo_sized: Foo<[uint, .. 4]> = Foo { foo: [1u,2,3,4] };
    let foo_unsized: &Foo<[uint]> = &foo_sized;
    for &i in foo_unsized.foo.iter() {
        println!("{}", i);
    }
}

@japaric
Copy link
Member

japaric commented Nov 9, 2014

@SimonSapin According to my understanding:

Box<Foo> is two words long (data + len), but you probably want to store header inline, i.e. Box<Foo> would have to be 3 words long (header + padding + data + len). AFAIK that's not possible with the current DST system.

Since Box<Foo> has layout data + len, data would have to be a pointer to (header + padding + slice_memory_chunk) allocated in the heap. I don't think this is easy to express, nor performant, you'll have to reallocate bytes to accommodate the header in the front.

But I could be wrong. You should ask @nick29581

@nrc
Copy link
Member

nrc commented Nov 17, 2014

@bkoropoff's suggestion is correct, the preferred way to create an unsized struct is via coercion. You won't be able to do *bytes either, since that would require creating a temporary on the stack of unknown size. We used to forbid structs like Foo completely, i.e., you had to have a type parameter so that the struct could be coerced from sized to unsized, but there was some use case for such structs (which I can't recall).

@nrc
Copy link
Member

nrc commented Nov 17, 2014

To make something actionable here, we should probably have a 'raw' struct (something like core::raw::Slice) so that it is possible to create a Box<Foo> via transmutes.

I don't see a way to allow creating such structs in a generic, safe way and I'm not sure it is something we should encourage. If there is a use case for this, then it would have to implemented as a language feature and would be post-1.0. If there is a strong use case, please file an issue on the RFCs repo.

@SimonSapin
Copy link
Contributor Author

Closing as I don’t think there is much actionable here. I opened this based on a “there has to be a better way” feeling, but understand that it pretty much has to be transmute or coercion since we can’t have generics over the set of fields in a struct.

@pnkfelix
Copy link
Member

pnkfelix commented Feb 21, 2017

If you happen upon this issue, there is some forward progress on e.g. rust-lang/rfcs#1808, rust-lang/rfcs#1909

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

No branches or pull requests

6 participants