Move `alloc::borrow` to `core`

I frequently encounter an issue when I am designing an allocation-free API that supports no_std with and without alloc, requiring the use of Cow.

The issue currently is, that Cow currently lives in alloc::borrow. So I have to choose between the API changing when activating alloc or design two different APIs with and without Cow.

Looking further into it, I discovered that alloc::borrow can actually live in core the only issue being coherence. As far as I am aware there is no concrete mechanism for the standard library to circumvent coherence rules in this case.

To be specific, the two problems are impl<T> ToOwned for [T] and impl ToOwned for str , which have to live in alloc .

What do you guys think, is this desirable? Is there an alternative solution? Thanks for @bjorn3 and @InfernoDeity for their help on Zulip.

3 Likes

One idea I had, though I'm not sure it's necessarily easier to get to work, would be to define Cow in core as

pub enum Cow<'a, B, O> {
    Borrowed(&'a B),
    Owned(O),
}

And then have alloc's Cow be something like type Cow<'a, B, O = <B as ToOwned>::Owned> = core::borrow::Cow<'a, B, O>;.

That would let ToOwned still live in alloc, which is arguably appropriate since if you wanted your own alloc library you'd probably want your own ToOwned too, in order to get [T] -> YourOwnVec<T>.

3 Likes

Well this is a very neat idea that solves the problem quiet well with an added bonus. This would also be quiet easy to implement.

I guess this is a bit premature, but would something like this need an RFC?

I believe the current process would be an API Change Proposal.

Thanks, I made one here: `Cow` for `core` · Issue #76 · rust-lang/libs-team · GitHub.

Wouldn't impls like From<&'a str> for Cow<'a, str> have some new coherence problems if Cow was defined in core?


Or, perhaps, that one's perhaps still possible if we were to generalize it. But what about AddAssign<&'a str> for Cow<'a, str>?


This one seems problematic as well:

impl<'a, T> FromIterator<T> for Cow<'a, [T]>
where
    T: Clone,

and there probably a few more examples.

That's a good point.

Apparently there is #[rustc_incoherent_impl], so actually implementing it isn't the problem. But specialization will very likely interfere with that. Considering I understand specialization only very poorly, I need to look into it to properly assess the scope of the problem here.

Hmm, since that would be for Cow<'_, str, String>, does that String make it local? Since it could only conflict with a blanket from core...

It doesn't make it local because coherence rules are not only about avoiding overlapping implementations but also about making sure that adding trait implementations with generic parameters on a type stays a non-breaking change.

It might be enough to kill the "breaking coherence here could/would be unsound due to specialization" argument though for this trait implementation.