flexstr icon indicating copy to clipboard operation
flexstr copied to clipboard

Custom Ref/Cow to preserve `&'static str`?

Open epage opened this issue 2 years ago • 13 comments

This was also one of they key early features for KString so that liquid could pass around data throughout the program and avoid allocations for this data.

epage avatar Mar 25 '22 19:03 epage

I'm not sure I understand. LocalStr and SharedStr are literally copying the &'static str ref on clone so it should be approx. the same speed as copying the ref itself (one extra machine word - 24 bytes vs 16 byte fat pointer)

nu11ptr avatar Mar 25 '22 19:03 nu11ptr

Maybe you are talking about onboarding? If you use from_static or local_str!/shared_str! these are compile time wrapper over the literal. Zero cost. They interop with other Local/Shared strings.

nu11ptr avatar Mar 25 '22 19:03 nu11ptr

KStringCow and KStringRef are roughly

enum KStringCow<'s> {
    Owned(Box<str>),
    Literal(&'static str),
    Borrowed(&'s str),
}

enum KStringRef<'s> {
    Literal(&'static str),
    Borrowed(&'s str),
}

This let's me have functions that return references that I can turn back into owned types without losing track of the 'static lifetime.

epage avatar Mar 25 '22 19:03 epage

Might need to see an example sometime. For some reason I'm just not getting it. If you borrow, you'd need to have a copy of the owned item or else the borrow wouldn't borrow. If you have a static...well, you have a static.

Are you talking something like my conditional ownership feature? (a side effect of using Rc/Arc) Essentially I advocate passing by &SharedStr as you can always clone() but the clone is just a ref count bump never an allocation/copy (another reason why I like Rc/Arc over Box I should add).

nu11ptr avatar Mar 25 '22 20:03 nu11ptr

In liquid, some data is dynamically generated and some comes from a previous allocation. I could still use a Cow<'s, SharedStr>. I'll have to think about the trade offs of that vs a custom Cow.

epage avatar Mar 25 '22 20:03 epage

Ok, if you can point me to a worked example or spot in the source that illustrates this. I'm not sure I 100% follow the use case (because in my mind, FlexStr is the antithesis of Cow in RO scenarios so tryign to understand why you'd want ot bring cow back into it, For example: why not pass &SharedStr instead? If you need an owned copy, clone, else use as is)

nu11ptr avatar Mar 25 '22 20:03 nu11ptr

In "liquid", you can directly access the elements of an Array or the members of an Object. There are also some implicit fields, .first, .last, and .size. Rather than store an extra element in the Object and Array for .size, .size is dynamically generated; there is no data to make a reference to.

See https://github.com/cobalt-org/liquid-rust/blob/master/crates/core/src/model/find.rs#L161

epage avatar Mar 25 '22 20:03 epage

but like I said, a Cow<'s, SharedStr> might work

epage avatar Mar 25 '22 20:03 epage

For more context on liquid, see https://shopify.github.io/liquid/

epage avatar Mar 25 '22 20:03 epage

I was thinking the other way around (passing in, not returning). I can definitely see use cases where you want to return a 'view' into something but don't necessarily want to copy it... or maybe you have conditional ownership (like what you are doing it seems).

Early on I played with ideas like having a 3rd and 4th type for this (I called it BStringy before someone took my stringy name and I went with FlexStr). I also played with the idea of integrating borrowing into my main type as a 4th variant but I didn't want the user to have to have deal with type annotations for non-borrowed use (I know they scare a lot of new Rust users).

I'll do more thinking on what makes sense, but not against bringing that idea back.

nu11ptr avatar Mar 25 '22 20:03 nu11ptr

(I know they scare a lot of new Rust users).

I would assume a new Rust user would generally not be using one of these crates :)

However, a couple items down on my priority list is creating yet-another string type, one focused on ease of use and general performance. The idea is to have as few lifetimes in this API as possible, so each string function would return an owned type. See https://docs.rs/eztd/latest/eztd/struct.String.html and https://epage.github.io/blog/2021/09/learning-rust/

epage avatar Mar 25 '22 20:03 epage

You kinda just described my goal with FlexStr :-) Just not done yet. Well, not exactly I guess. I'm going more for easy String replacement with better performance in immutable usage scenarios, not focusing on ease of use particularly.

nu11ptr avatar Mar 25 '22 21:03 nu11ptr

I figured out how to support borrowed strings. In hindsight it was obvious. Will be in next release.

nu11ptr avatar Mar 26 '22 12:03 nu11ptr