Skip to content

Make FixedSizeArray an unsafe trait #28538

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

Merged
merged 2 commits into from
Sep 24, 2015

Conversation

alevy
Copy link
Contributor

@alevy alevy commented Sep 20, 2015

[breaking-change]

FixedSizeArray is meant to be implemented for arrays of fixed size only, but can be implemented for anything at the moment. Marking the trait unsafe would make it more reasonable to write unsafe code which operates on fixed size arrays of any size.

For example, using uninitialized to create a fixed size array and immediately filling it with a fixed value is externally safe:

pub fn init_with_nones<T, A: FixedSizeArray<Option<T>>>() -> A {
    let mut res = unsafe { mem::uninitialized() };
    for elm in res.as_mut_slice().iter_mut() {
        *elm = None;
    }
    res
}

But the same code is not safe if FixedSizeArray is implemented for other types:

struct Foo { foo: usize }
impl FixedSizeArray<Option<usize>> for Foo {
    fn as_slice(&self) -> &[usize] { &[] }
    fn as_mut_slice(&self) -> &mut [usize] { &mut [] }
}

now init_with_nones() : Foo returns a Foo with an undefined value for the field foo.

@rust-highfive
Copy link
Contributor

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @pcwalton (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. The way Github handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@bluss
Copy link
Member

bluss commented Sep 20, 2015

Can you put [breaking-change] (exactly) in the commit log or the PR message? Thanks.

This sounds good to me, but I don't think I can make the decision.

@alevy
Copy link
Contributor Author

alevy commented Sep 20, 2015

@bluss I added it to the PR message, hopefully correctly...

@petrochenkov
Copy link
Contributor

Hm, if you break it anyway maybe you can make T an output parameter? FixedSizeArray<T> and FixedSizeArray<U> where T != U can never be implemented for the same type.

@alevy
Copy link
Contributor Author

alevy commented Sep 20, 2015

@petrochenkov Seems reasonable except I'm not able to write this such that it comiles atm:

unsafe impl<E, A: Unsize<[E]>> FixedSizeArray for A {
    type T = A;

    #[inline]
    fn as_slice(&self) -> &[Self::T] {
        self
    }
    #[inline]
    fn as_mut_slice(&mut self) -> &mut [Self::T] {
        self
    }
}

Results in a compile error citing E0207 -- that E is not bound by the impl trait, self type or perdicate. This may be a bug in RFC 447, which doesn't seem to consider bounds by other types (in this case, the type of E is determined uniquely, I think)

@bluss
Copy link
Member

bluss commented Sep 20, 2015

@alexcrichton I'd like us to do this

@petrochenkov
Copy link
Contributor

@alevy
That's bad. There may be a workaround, but I don't know it (people on IRC may know). The patch looks good enough to me even without this change.

@alexcrichton
Copy link
Member

Can you also expand the documentation to indicate why this trait is unsafe? e.g what guarantees must implementors uphold and what guarantees can code rely on?

I'm a little wary to continue to tweak perma-unstable traits like this because they're permanently unstable and lots of tweaks mean that the abstraction is useful and should perhaps live externally on crates.io first before moving into the standard library. This trait was added some time ago and there's no current usage in-tree, so there's no real reason this can't be external.

For now though an incremental improvement is fine.

@alevy
Copy link
Contributor Author

alevy commented Sep 23, 2015

@alexcrichton I expanded the documentation. Happy to revise if needed, though. Thanks!

@alexcrichton
Copy link
Member

@bors: r+ b30d896

Thanks!

bors added a commit that referenced this pull request Sep 24, 2015
[breaking-change]

`FixedSizeArray` is meant to be implemented for arrays of fixed size only, but can be implemented for anything at the moment. Marking the trait unsafe would make it more reasonable to write unsafe code which operates on fixed size arrays of any size.

For example, using `uninitialized` to create a fixed size array and immediately filling it with a fixed value is externally safe:

```
pub fn init_with_nones<T, A: FixedSizeArray<Option<T>>>() -> A {
    let mut res = unsafe { mem::uninitialized() };
    for elm in res.as_mut_slice().iter_mut() {
        *elm = None;
    }
    res
}
```

But the same code is not safe if `FixedSizeArray` is implemented for other types:

```
struct Foo { foo: usize }
impl FixedSizeArray<Option<usize>> for Foo {
    fn as_slice(&self) -> &[usize] { &[] }
    fn as_mut_slice(&self) -> &mut [usize] { &mut [] }
}
```

now `init_with_nones() : Foo` returns a `Foo` with an undefined value for the field `foo`.
@bors
Copy link
Collaborator

bors commented Sep 24, 2015

⌛ Testing commit b30d896 with merge 6a21874...

@bors bors merged commit b30d896 into rust-lang:master Sep 24, 2015
@alevy alevy deleted the make_fixedsizearray_unsafe branch September 24, 2015 21:13
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.

7 participants