How to convert Cow<'_, T> to Cow<'static, T>?

let input: Cow<'_, str> = String::from("123").into();
let output: Cow<'static, str> = if '_ == 'static {
    input
}else{
    input.into_owned().into()
};

Cow::Owned(input.into_owned()) should work. It would create an owned version of a static string literal, though.

2 Likes

It needn't memory copy when '_ == 'static.

But when you ask "How to convert Cow<’_, T> to Cow<’static, T>?", you're asking about the general case. And in general, @jhpratt is right, taking ownership is the only way to make it happen for any given Cow lifetime 'a.

What you say is true in principle, but AFAIK there's no way to inform the compiler of this on stable Rust.

1 Like

In that very specific example, the lifetime is irrelevant anyway because it is already owned.

Note that this fundamentally can't be done. Lifetimes are erased at runtime, so there's no way to run logic on them.

5 Likes

The lifetime is not irrelevant even if the value is owned. This is because that lifetime still infects whatever type uses it in one of its fields, or it would have to be defined as Cow<'static, T>, which is possible but is also a markedly more restricted type. You'd also have to ask yourself why, in that case, you're not using String or some such instead.

@scottmcm not even with specialization on nightly?

Well, historically you could with specialization on nightly -- and that's the biggest reason specialization isn't stable, because it's unsound to specialize on lifetimes. See https://github.com/rust-lang/rust/issues/31844

(Maybe at some point some one might find a way to make a sound version, but right now there's no known model for specialization on lifetimes that actually works.)

4 Likes

I think the what I would say is it's irrelevant in the sense that the lifetime isn't inhabited (but you do have to determine it when you create it), and that Cow::Owned(input.into_owned()) is "free".

I think a better example of what is trying to be done is needed. I don't really understand why the conversion is being asked for.

It's because Cow<'static, T> and Cow<'a, T> are separate types, in a similar way that Cow<'static, str> and Cow<'static, Path> are different types.

In general, any generic type isn't a type at all: it's a type-level fn that constructs types, or alternately (but equivalently) as an infinite set of potential types¹.

¹ Note that the latter interpretation is likely more confusing, since traits are also viewable as sets of types. But generic types and traits work together rather than providing the same functionality twice.

2 Likes

I probably lack imagination here. I'm not seeing the use case for converting Cow<'_, T> into Cow<'static, T> in the first place. It's not something I've really encountered personally. I guess maybe it'd be hypothetically useful in allowing you to store it in a 'static/owned context elsewhere.

But, when it comes to using it, you'd still have to deal with the owned value (so you can't always obtaine a &'static T from it) which seems to limit the utility to just that?

It is free when input = Cow::Borrowed(&'_ str).
It is free when input = Cow::Owned(String).
But, It isn't free when input = Cow::Borrowed(&'static str).

I don't know why the code is asking that of the OP. In order to be able to tell, we'd need to see more of the context i.e. At least the fn that the original code snippet appears in, but possibly more.

Yes, sorry, it was an offhand comment about your specific example, which probably wasn't very helpful. In your very specific example, it's already Cow::Owned(String), so:
(1) any lifetime can be chosen at compile time for '_
(2) into_owned() is just going to move the value from one Cow to another

I think some additional context may help here because at least to me, I'm not certain what your actual end goal is, but as has already been stated by more helpful answers, unless it's already typed as 'static at compile time anyway, you're out of luck. (Edit: also I suppose you're out of luck in not being able to specialize on it at compile time either)

(I'm 99% sure this was not what you were asking for.)

Lifetimes not only describe how long something lives, but how long you're allowed to use it for.

When 'static is borrowed for 'a lifetime, it's not safe to go back from 'a to 'static, even if you know this object had a 'static lifetime. This is because lifetimes are also used for integrity and safety of constructs like guards and re-borrows. For example, a &'static mut could have been re-borrowed for 'a lifetime, which was borrowed as immutable Cow<'a>. Extending that back to 'static would break borrow checking and aliasing rules.

So there is no faster safe and sound method than Cow::Owned(input.into_owned()).

2 Likes

Note that if you have a particular use for preserving 'static references, you can define a type that does that explicitly, with a third variant vs. Cow's two:

enum Cow2<'a, T> {
  Static(&'static T),
  Borrowed(&'a T),
  Owned(T),
}

This way the distinction between borrows and statics/constants is preserved at run time, making it safe and simple to treat them differently.

If you care about this much copy-avoidance, you might also want to make the owned variant be Owned(Arc<T>). (Is there a crate that provides this? I've almost ended up implementing it myself. The theoretical advantage over a bare Arc is avoiding some refcount updates and being able to construct instances at compile time, but there's further considerations to making it actually "optimal".)

2 Likes

I think you may need follows:

trait MyInto<T>{
  fn _into(self)->T;
}

impl<T> MyInto<Cow<'static,T>> for Cow<'static,T>{
  fn _into(self)->Cow<'static,T>{
    self
  }
}

impl<T> MyInto<Cow<'static,T>> for Cow<!'static,T>{
  fn _into(self)->Cow<'static,T>{
    self.into_owned().into()
  }
}

Unfortunately, negative lifetime bounds are not support. Moreover, negative type bounds haven't been supported yet, let alone supporting lifetime bounds. But it may not excluded if you're using nightly compiler, which I haven't test on yet, but I guess it won't support as well.

Since above, the best and simplest way should be to call into_owned uniformly.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.