-
Notifications
You must be signed in to change notification settings - Fork 183
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
Use pattern in icu_decimal
#5385
Conversation
utils/pattern/src/frontend/mod.rs
Outdated
use core::{ | ||
convert::Infallible, | ||
fmt::{self, Write}, | ||
marker::PhantomData, | ||
}; | ||
use writeable::{adapters::TryWriteableInfallibleAsWriteable, PartsWrite, TryWriteable, Writeable}; | ||
|
||
#[allow(clippy::unwrap_used, clippy::indexing_slicing)] | ||
impl<Store> SinglePlaceholderPattern<Store> where Store: Deref<Target = str> { |
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.
@sffc how do I do this nicely? Are there encoding helpers?
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.
Use Pattern::try_from_items
and then Pattern::take_store
#[cfg_attr(feature = "serde", serde(borrow))] | ||
pub minus_sign_affixes: AffixesV1<'data>, | ||
pub minus_sign_pattern: Option<SinglePlaceholderPattern<Cow<'data, str>>>, |
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.
strictly speaking this should use a pattern type that requires exactly one placeholder
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.
When you make .suffix()
return an Option
, you can debug_assert that it is Some
.
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.
#[cfg_attr(feature = "serde", serde(borrow))] | ||
pub minus_sign_affixes: AffixesV1<'data>, | ||
pub minus_sign_pattern: Option<SinglePlaceholderPattern<Cow<'data, str>>>, |
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.
Question: why did you make it an Option
? The value is only 2 bytes long. Seems like unnecessary indirection? You already have a Cow
so the niche is already being used.
impl SinglePlaceholderPattern<str> { | ||
pub fn encode_store(prefix: &str, suffix: &str) -> alloc::boxed::Box<str> { | ||
alloc::format!( | ||
"{}{prefix}{suffix}", | ||
char::from_u32(prefix.len() as u32 + 1).unwrap() | ||
) | ||
.into_boxed_str() | ||
} | ||
} |
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.
Issue: I would prefer to avoid adding methods directly to SinglePlaceholderPattern<str>
. Methods should be added to Pattern<Store>
.
Besides that, this code duplicates logic that lives in the pattern constructor. You should build the pattern using the standard APIs and then use take_store
.
The constructor you probably want is Pattern::try_from_items
.
#[allow(clippy::unwrap_used, clippy::indexing_slicing)] | ||
impl<Store> SinglePlaceholderPattern<Store> | ||
where | ||
Store: Deref<Target = str>, |
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.
Issue: The bound I use everywhere else is AsRef<str>
.
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.
I don't like that bound.
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.
We should open a discussion about whether we want the backend to be
AsRef<str>
Deref<Target = str>
Borrow<str>
str
But for now please remain consistent.
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.
Backend::Store
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, for all of those I mean a substitution of str
with Backend::Store
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.
Just to start the commentary a bit:
I went with AsRef<Backend::Store>
because it is the most flexible.
Why AsRef: The Store
parameter worked out well for me in LiteMap and ZeroTrie before embarking on Pattern. It makes certain impls a pain to write, but once they are written, things "just work" and are very easy to use and very flexible.
Why not Deref: I don't care that the store dereferences uniquely to Backend::Store
. For instance, at some point we might get Box<AsciiStr>
which derefs to AsciiStr
before str
.
Why not Borrow: I don't care about the additional invariants on Borrow like Hash and Eq. If I want to implement those traits on Pattern, I can; I don't need Borrow's help.
Why not DSTs: I didn't go with the dynamically sized type because I dislike the amount of unsafe
code required for the DSTs to work. I have made multiple attempts to write safe abstractions for ref casts and box casts, but they keep being shot down (#5101). I would like to block the pure DST approach on first solving #2778 / #2310 / #5127, but I'm open to it assuming it can be written in a way that minimizes unsafe
.
if let Some(index) = index.checked_sub(1) { | ||
&chars.as_str()[index..] | ||
} else { | ||
chars.as_str() |
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.
Issue: this returns the same string for both prefix
and suffix
if there is no placeholder. I think suffix()
should return Option<&str>
.
let index = chars.next().unwrap() as usize; | ||
if let Some(index) = index.checked_sub(1) { | ||
&chars.as_str()[..index] | ||
} else { | ||
chars.as_str() | ||
} |
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.
Issue: this code is not panic-proof. There are constructors that are not unsafe
; you can make a Pattern pointing to an invalid store, and the docs say "unexpected behavior may occur" but not "panics may occur".
Please resolve the two fallible array getters in this function to debug-assert and then return ""
.
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.
I know, that's why I asked how to do this properly
let index = chars.next().unwrap() as usize; | ||
if let Some(index) = index.checked_sub(1) { | ||
&chars.as_str()[index..] |
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.
Issue: Please change these two fallible array getters to debug-assert and then return either None
or Some("")
.
#[cfg_attr(feature = "serde", serde(borrow))] | ||
pub minus_sign_affixes: AffixesV1<'data>, | ||
pub minus_sign_pattern: Option<SinglePlaceholderPattern<Cow<'data, str>>>, |
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.
When you make .suffix()
return an Option
, you can debug_assert that it is Some
.
It's also worth noting that the "correct" way to use Pattern, like ListFormatter, makes use of nested writeables. Kartavya did a good job with nested writeables in #5383. You can do this here by making a private type |
I don't think there's anything wrong with this.
I can, but this will increase code complexity and very likely also code size. |
I think you're overestimating the code complexity of nested writeables. I'm quite happy with how it went in Kartavya's open PR and it reduces the total number of lines of code. I don't think it will have an impact on code size, assuming everything is sufficiently inlined. But this is purely a code cleanup thing, not a data representation thing, so it doesn't block the PR which is more about the data representation. |
Discussion: we'll pursue stuffing all the strings in a multi-field-VarULE instead |
#5256