https://doc.rust-lang.org/src/alloc/vec.rs.html#1365-1367
Technically Vec::len()
could be a const fn
, is there any reason why that's not the case or was it just overlooked? Thanks.
https://doc.rust-lang.org/src/alloc/vec.rs.html#1365-1367
Technically Vec::len()
could be a const fn
, is there any reason why that's not the case or was it just overlooked? Thanks.
The reason is that Vec cannot be constant except for empty vec.
Yes, but that's not what I need it for. but conversions into other custom types could be const:
#[repr(C)]
pub struct BlahVec {
ptr: *const Blah,
len: usize,
cap: usize,
destructor: BlahDestructor,
}
#[repr(C, u8)]
pub enum BlahDestructor {
DefaultRust,
NoDestructor,
External(extern "C" fn(*mut BlahVecDestructor)),
}
impl BlahVec {
#[inline(always)]
pub const fn new() -> Self {
Self::from_vec(Vec::<Blah>::new())
}
// I want this conversion fn to be const, that's why
#[inline(always)]
pub const fn from_vec(v: Vec<Blah>) -> BlahVec {
use std::mem::ManuallyDrop;
// note: &[T]::as_ref() is a const fn, Vec<T>::as_ptr() is not?
let ptr = (&v[..]).as_ptr();
let len = (&v[..]).len();
let cap = (&v[..]).capacity();
let _ = ManuallyDrop::new(v);
BlahVec {
ptr,
len,
cap,
destructor: BlahVecDestructor::DefaultRust,
}
}
}
impl Drop for BlahVec {
fn drop(&mut self) {
match self.destructor {
BlahVecDestructor::DefaultRust => { let _ = unsafe { Vec::from_raw_parts(self.ptr as *mut Blah, self.len, self.cap) }; },
BlahVecDestructor::NoDestructor => { },
BlahVecDestructor::External(f) => { f(self); }
}
}
}
I'm then creating the Vec from a &'static [u8]
, so the len() is non-zero and also known at compile time. Yes I know this is all unsafe, but I'm not using dynamic allocation.
So you might want to use ArrayVec or the likes, which uses stack as storage.
Well I've solved it now by making two separate functions, one for new()
and one for new_nonconst()
:
impl BlahVec {
#[inline(always)]
pub fn new() -> BlahVec {
use std::mem::ManuallyDrop;
let v = Vec::new();
let md = ManuallyDrop::new(v);
BlahVec {
ptr: md.as_ptr(),
len: 0,
cap: 0,
destructor: $destructor_name::DefaultRust,
}
}
}
I can't use ArrayVec
or similar because I also need to handle pointers to memory not managed by Rust (i.e. allocated by a C++ std::vector
) as well as pointers into static memory (i.e. &'static [T]
) as well as regular Rust Vec<T>
in one type with as little conversion code as possible.
I can set the ptr
and cap
to 0, but I can't get the .get_ptr()
in a const fn. Right now I've made everything non-const and hope that the optimizer catches it, it works but it's not a great solution.
Unless you're putting the result into a const
context (i.e. you have const SOMETHING
or static SOMETHING
at the use site), const
has no impact on the pre-evaluation of the value. If it's inlined to a precomputed value, that's on the optimizer to do, even if you mark it const
.
(IOW, const
does not mean "constant evaluate this if all inputs are constant", it means "this is constant evaluable, only such that it can be put in a constant compiletime value".)
There are vague murmurings of a future const {}
block which would force constant evaluation of its contents (roughly by making it a const context) even in non-const contexts.
They're a little further along than that
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.