-
-
Notifications
You must be signed in to change notification settings - Fork 17
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
Implements const_deref
feature
#49
base: master
Are you sure you want to change the base?
Conversation
A less duplicated approach (but significantly more complex) would be to use a tt muncher to strip macro_rules! cfg_const_trait {
( $($tt:tt)* ) => { cfg_const_trait_munch! { [] [$($tt)*] } };
}
#[cfg(not(feature = "const_deref"))]
macro_rules! cfg_const_trait_munch {
( [$($done:tt)*] [] ) => { $($done)* };
// one arm is technically sufficient but more arms reduce recursion depth
( [$($done:tt)*] [ ~const $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* ] [$($todo)*] } };
( [$($done:tt)*] [$a:tt ~const $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* $a ] [$($todo)*] } };
( [$($done:tt)*] [$a:tt $b:tt ~const $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* $a $b ] [$($todo)*] } };
( [$($done:tt)*] [$a:tt $b:tt $c:tt ~const $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* $a $b $c ] [$($todo)*] } };
( [$($done:tt)*] [$a:tt $b:tt $c:tt $d:tt $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* $a $b $c $d] [$($todo)*] } };
( [$($done:tt)*] [$a:tt $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* $a ] [$($todo)*] } };
}
#[cfg(feature = "const_deref")]
macro_rules! cfg_const_trait_munch {
( [$($done:tt)*] [$($todo:tt)*] ) => { $($done)* $($todo)* };
} |
Cool. But it doesn't work with |
I'm not pro in macros, can you explain what the reason may be |
It's because all this macro is doing is going through the tokens and removing Essentially, all the macro is doing is taking tokens from the right Since the macro only has to munch over the function declaration (since it jumps the body in on token tree), the following version works fine and is easier to follow: macro_rules! cfg_const_trait {
( $($tt:tt)* ) => { cfg_const_trait_munch! { [] [$($tt)*] } };
}
#[cfg(not(feature = "const_deref"))]
macro_rules! cfg_const_trait_munch {
( [$($done:tt)*] [] ) => { $($done)* };
( [$($done:tt)*] [~const $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* ] [$($todo)*] } };
( [$($done:tt)*] [ const fn $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* fn] [$($todo)*] } };
( [$($done:tt)*] [$a:tt $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* $a] [$($todo)*] } };
}
#[cfg(feature = "const_deref")]
macro_rules! cfg_const_trait_munch {
( [$($done:tt)*] [$($todo:tt)*] ) => { $($done)* $($todo)* };
} The additional arms are merely to make macro expansion take The simplest way to allow wrapping |
Thanks, but this is too detailed. macro_rules! cfg_const_trait_munch {
( [$($done:tt)*] [] ) => { $($done)* };
// one arm is technically sufficient but more arms reduce recursion depth
( [$($done:tt)*] [~const $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* ] [$($todo)*] } };
( [$($done:tt)*] [ const $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* ] [$($todo)*] } };
( [$($done:tt)*] [$a:tt $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* $a] [$($todo)*] } };
} |
|
This confused me, because he was still able to remove |
Whoops, I forgot to put a link in at all (it was just The trick about not recursing into this version gets strippedimpl<'a, T, U> Cow<'a, T, U>
where
T: Beef + ?Sized,
U: Capacity,
{
cfg_const_trait! {
/// Borrowed data.
///
/// # Example
///
/// ```rust
/// use beef::Cow;
///
/// let borrowed: Cow<str> = Cow::borrowed("I'm just a borrow");
/// ```
///
#[inline]
pub const fn borrowed(val: &'a T) -> Self
where T: ~const Beef,
{
todo!()
}
}
} but this version doesn't.cfg_const_trait! {
impl<'a, T, U> Cow<'a, T, U>
where
T: Beef + ?Sized,
U: Capacity,
{
/// Borrowed data.
///
/// # Example
///
/// ```rust
/// use beef::Cow;
///
/// let borrowed: Cow<str> = Cow::borrowed("I'm just a borrow");
/// ```
///
#[inline]
pub const fn borrowed(val: &'a T) -> Self
where T: ~const Beef,
{
todo!()
}
}
} It is probably better to just apply the macro at each level it is needed, rather than adding a recursive call, but it is possible to implement by directly adding the recursion. (This has to be before the fallback arm, or else it will never be taken.) ( [$($done:tt)*] [{$($a:tt)*} $($todo:tt)*] ) => { cfg_const_trait_munch! { [$($done)* { cfg_const_trait!{$($a)*} }] [$($todo)*] } }; |
all this time I misunderstood |
Yep, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to reviewers: use "ignore whitespace changes" mode.
// for `~const Steak` | ||
// I think that Steak (cooked beef) would contain "cooked" functions | ||
// (computed at compile-time) | ||
// However, `Vec::new()` also has const len/cap, but `std` does not implement it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thought so far has been to not put const
on methods where the type isn't constructible const
(or in Vec
's case, can only give one answer in const
contexts).
I suspect this will relax in the future, but for the time being it allows greater impl flexibility especially w.r.t. future const allocation.
Co-authored-by: Christopher Durham <cad97@cad97.com>
@CAD97 Is panic considered an allowed style in |
It depends a bit. If the panic is a static string and does not need any formatting (i.e. Current practice seems to be that such methods should be made const only if
So I'd say If our Footnotes
|
But then it will prevent non-const implementation. |
I don't find a way to do something similar to |
But it doesn't look like good code |
I found a super unstable magic function)) |
It uses in |
Note that But yes, since it's not possible at the moment to reconstruct the #![feature(const_refs_to_cell)] // new
impl<'a, T, U> Cow<'a, T, U>
where
T: Beef + ?Sized,
U: Capacity,
{
#[inline]
pub const fn as_borrowed(&self) -> Option<&'a T>
where
T: ~const Steak,
U: ~const Capacity,
{
if self.is_borrowed() {
Some(self.borrow())
} else {
None
}
}
#[inline]
pub const fn unwrap_borrowed(self) -> &'a T
where
T: ~const Steak,
U: ~const Capacity,
{
match self.as_borrowed() {
Some(borrowed) => borrowed,
None => panic!("called `unwrap_borrowed` on an owned `beef::Cow`"),
}
}
#[inline]
pub const fn is_borrowed(&self) -> bool
where
U: ~const Capacity,
{
self.capacity().is_none()
}
#[inline]
const fn borrow(&self) -> &T
where
T: ~const Steak,
{
unsafe { &*T::ref_from_parts::<U>(self.ptr, self.fat) }
}
#[inline]
const fn capacity(&self) -> Option<U::NonZero>
where
U: ~const Capacity,
{
U::maybe(self.fat, self.cap)
}
} |
I thought the same thing |
#[cfg(feature = "const_deref")] | ||
use core::marker::Destruct; | ||
use core::marker::PhantomData; | ||
#[cfg(not(feature = "const_deref"))] | ||
use core::marker::Sized as Destruct; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CAD97 is there anything more invasive than Sized
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where Self: Sized
is in fact a trivial bound that doesn't add any restrictions on the ungated impl, if that's what you're asking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I just don't know anything more trivial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
T: ?Sized
is the only weaker bound than T: ?Sized
currently, or just T:
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but we can't create alias for this . On the other hand, Destruct
used in one function.
// fixme: it should be unsafe :) | ||
const fn from_parts(ptr: NonNull<T::PointerT>, fat: usize, cap: U::Field) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maciejhirsz uses a lot of unsafe in the code, believing that Cow
is created only in borrowed
/owned
In this case, it is reasonable to make from_parts
unsafe
src/generic.rs
Outdated
let Cow { ptr, fat, cap, .. } = self; | ||
(*ptr, *fat, *cap) | ||
} | ||
|
||
// fixme: required `borrowed_from_raw_parts` - it has bug with the processing of structs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// fixme: required `borrowed_from_raw_parts` - it has bug with the processing of structs | |
// fixme: required `from_parts` - `cfg_const_deref` has problem in the processing of structs | |
`` |
src/generic.rs
Outdated
where | ||
T: ~const Steak, | ||
U: ~const Capacity, | ||
<T as ToOwned>::Owned: ~const Destruct |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is necessary? rustc
suggest use it.
src/generic.rs
Outdated
let (ptr, fat, cap) = Self::std_into_parts::<T, U>(cow); | ||
Self::rt_from_cow(ptr, fat, cap) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there is a way
(The moment the code uses (I'm still not comfortable using (Generally, any feature without a tracking issue is a no-touch.) |
Oh, no. It is sad)) |
May be users will be use |
I re-read |
@CAD97 why some functions require |
Roughly, mutability behind an indirection ( Starting with
As the feature gate error says, see rust-lang/rust#80384 for more information. |
// fixme: it should be unsafe :) | ||
const fn from_parts(ptr: NonNull<T::PointerT>, fat: usize, cap: U::Field) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// fixme: it should be unsafe :) | |
const fn from_parts(ptr: NonNull<T::PointerT>, fat: usize, cap: U::Field) -> Self { | |
const unsafe fn from_raw_parts(ptr: NonNull<T::PointerT>, fat: usize, cap: U::Field) -> Self { |
Obviously the change needs to be propagated. Even though field access is safe and it is formally acceptable to have non-exported safe functions with safety requirements, it's much better practice to mark them unsafe
.
const_deref
feature should add almost allconst
functionality fromstd::borrow::Cow
Sorry for the code duplication