I want to specialize &'static str
from &'a str
. Something like this:
use std::borrow::Cow;
struct MyString {
inner: Cow<'static, str>,
}
impl From<&'static str> for MyString {
fn from(x: &'static str) -> Self {
MyString {
inner: Cow::Borrowed(x),
}
}
}
impl<T: Into<String>> From<T> for MyString {
fn from(x: T) -> Self {
MyString {
inner: Cow::Owned(x.into()),
}
}
}
fn main() {
match MyString::from("foo").inner {
Cow::Borrowed(..) => (),
_ => {
panic!();
}
}
let s = String::from("bar");
match MyString::from(s.as_ref()).inner {
Cow::Owned(..) => (),
_ => {
panic!();
}
}
match MyString::from(String::from("qux")).inner {
Cow::Owned(..) => (),
_ => {
panic!();
}
}
}
The gist is that MyString
stores a statically-allocated string literal as a &'static str
and all other strings as a String
. This allows MyString
to avoid having a lifetime parameter—i.e., MyString<'a>
, which is critical for my API, all while allowing the caller to pass in any kind of string and have MyString
automatically do the correct thing.
The problem is that the code doesn't compile:
error[E0119]: conflicting implementations of trait `std::convert::From<&'static str>` for type `MyString`:
--> src/main.rs:15:1
|
7 | impl From<&'static str> for MyString {
| ------------------------------------ first implementation here
...
15 | impl<T: Into<String>> From<T> for MyString {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyString`
Is there any trick that allows me to do what I want? If not, is lifetime specialization something that Rust will ever support?
Rust 1.25.0 does not have specialization of any kind. If I'm reading the specialization RFC correctly, then lifetime specialization will not be supported even when the RFC is implemented:
(Emphasis mine)
There's some examples further in the link that indicate some of the concrete issues.
I recommend using a
Cow
to handle the "owned or borrowed" case.I write this answer after reading this duplicated post asking how to define a method/function that behaves differently when it is passed a static string or a non-static string.
This is not possible, so a workaround may be using a wrapper type to wrap the string argument in an
enum
:MyString
stores a statically-allocated string literal as a&'static str
and all other strings as aString
.As pointed in the comments below, the standard library provides a type that fits the borrowed/owned case: the smart pointer
Cow
.The enum
MyString
used in this example is just a specific enum for managing string types.The only difference stems from a somewhat more specific naming of the enum and its variants related to the specific usage:
MyString::Static("forever")
versusCow::Borrowed("forever")
andMyString::Heap(str)
versusCow::Owned(str)
.Does this help improve mnemonics and code readability? I'm quite sure that this holds only for novices or occasional Rust programmers, not for seasoned Rustaceans.