-
Notifications
You must be signed in to change notification settings - Fork 49
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
Add to ArrayVec
a from_array_empty
Constructor as const fn
#141
Conversation
Also lifts the `A: Array` constraint on ArrayVec as it was not used yet and would otherwise conflict with the const fn, which currently (Rust 1.51) may not have any trait constraints. Since this commit only adds a new function and lifts a constraint, this should be perfectly backwards compatible.
What's happening is that you're lifting the bound on the type but still enforcing it on all the methods of the type. Which does come out to "mostly correct", except that you can no longer assume that the data is A:Array. That is, a person would be allowed to construct an ArrayVec for some data type without it being an array and then all the methods just wouldn't exist for that instantiation. I'm not sure that I should accept this as is. Removing the bound from the type seems odd to do because now you can construct incorrect ArrayVec values that have no methods. I could accept this constructor without it being const fn, because it is a useful constructor. |
Well, it is not just "mostly correct", it is completely correct. So, if the requirement Furthermore, well as a rule of thumb, if a struct in the std-lib does not require any trait-bounds it will not have it. Take for example Yet, another example is So, from a correctness point of view, there is no need for On the other hand, by lifting this (functionally irrelevant) constraint, the |
However, since it was initially published with the A:Array bound, and since it's marked as repr(C) specifically so that the type's fields can be directly manipulated using unsafe code in other crates, it feels a little off to relax this bound. I do sympathize with the need to put this in a const or a static though. Would you be interested in using an alternate approach with having a feature to make the fields of the ArrayVec just be |
I'm not quite sure what you are referring to, since none of the fields are accessible for downstream crates, not even in On the other hand the But still when using the struct MyVec {/* private magical stuff */}
/// A weird function that needs a `thingy` that upholds a very very important contract.
/// Safety: Only ever call this, if and only if you are sure that **the** contract is uphold
/// (that make this function `unsafe`).
/// However (for whatever reason) this contract is perfectly uphold for any ArrayVec<A> with A:Array
pub unsafe fn weird_foo(thingy: *mut MyVec) {/* do some back magic */} A perfectly safe wrapper for it would be: use tinyvec::{Array,ArrayVec};
/// A perfectly safe function (that's the power of Rust)
pub fn a_safe_foo<A:Array>(vec: &mut ArrayVec<A>) {
unsafe {
// This is sound, because here, it is enforced that A:Array holds, no matter what,
// and so the contract is fullfilled
weird_foo(vec as *mut ArrayVec<A> as *mut MyVec)
}
} And so, this wrapper function
I don't think, that this would address your concerns either, because that would really enable anybody to even arbitrarily manipulate all fields of Imagine that I personally, don't see any good reason for opening these fields, and it might even have real consequences, unlike the approach I propose in this PR. |
The way I've encode marker bounds in So you would get: // ?Sized because Array doesn't have a Sized supertrait
pub struct IsAnArray<A: ?sized>(PhantomData<fn() -> A>);
impl<A: ?sized + Array> IsAnArray<A> {
pub const NEW: Self = Self(PhantomData);
}
pub trait Array {
// Not absolutely required for this workaround
const IMPLS: IsAnArray<Self> = IsAnArray::NEW;
..... rest of the associated items
} impl<A> ArrayVec<A> {
/// # Example
///
/// ```
/// use tinyvec::{Array, ArrayVec};
///
/// static ARR: ArrayVec<[u8; 4]> =
/// ArrayVec::from_array_empty([0, 1, 2, 3], Array::IMPLS);
///
/// assert_eq!(ARR.len(), 0);
/// assert_eq!(ARR, []);
///
/// ```
///
pub const fn from_array_empty(data: A, _: IsAnArray<A>) -> Self {
Self{data, len: 0}
}
} |
Alright I got out voted when I asked around, so we'll go with this. |
This PR adds a new constructor
from_array_empty
to theArrayVec
, which takes anArray
by value and uses it only as storage space, thus initializing with zero length (i.e. empty). Since this is generally valid unlike thefrom_array_len
(which requires an additional check), this check-free version now is eligible to become aconst fn
to allowArrayVec
even instatic
context.However, since
const fn
s may not have any trait bounds (as of Rust 1.51), theA: Array
trait bound on theArrayVec
struct itself, is lifted, apparently, it was never used tho.Since this PR only adds a new function and lifts a constraint, this change should be perfectly backwards compatible.