After struggling with Deref
, Borrow
, and AsRef
myself for a while, I would be interested in why you think (or thought) that Deref
is needed (or for what purpose you would like to implement Deref
).
Perhaps it's not needed in your case, and given your example (not knowing what you want to do later), I would likely write it as follows:
use std::fmt;
enum MyEnum{
F(f64),
I(i64),
S(String)
}
impl fmt::Display for MyEnum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MyEnum::F(x) => write!(f, "{x}"),
MyEnum::I(x) => write!(f, "{x}"),
MyEnum::S(x) => f.write_str(x),
}
}
}
fn main(){
let a = MyEnum::F(1.1);
let b = MyEnum::I(100);
let c = MyEnum::S(String::from("hello world"));
println!("{}", a);
println!("{}", b);
println!("{}", c);
}
(Playground)
Now that is if you want to have the string representation for printing it somewhere. Implementing std::fmt::Display
automatically makes a type implementing ToString
(thus also allowing you to use .to_string()
if you like.
Another option would be to use From
, possibly in combination with Cow
:
use std::borrow::Cow;
enum MyEnum{
F(f64),
I(i64),
S(String)
}
impl From<MyEnum> for String {
fn from(value: MyEnum) -> String {
match value {
MyEnum::F(x) => x.to_string(),
MyEnum::I(x) => x.to_string(),
MyEnum::S(x) => x,
}
}
}
impl<'a> From<&'a MyEnum> for Cow<'a, str> {
fn from(value: &'a MyEnum) -> Cow<'a, str> {
match value {
MyEnum::F(x) => Cow::Owned(x.to_string()),
MyEnum::I(x) => Cow::Owned(x.to_string()),
MyEnum::S(x) => Cow::Borrowed(&x),
}
}
}
fn main(){
let a = MyEnum::F(1.1);
let b = MyEnum::I(100);
let c = MyEnum::S(String::from("hello world"));
// These work on references:
println!("{}", Cow::<str>::from(&a));
println!("{}", Cow::<str>::from(&b));
println!("{}", Cow::<str>::from(&c));
// These will consume the corresponding value:
println!("{}", String::from(a));
println!("{}", String::from(b));
println!("{}", String::from(c));
}
(Playground)
The difficulty is that you cannot return a reference to a str
slice when the enum value is a float or integer (because you will have to create a String
and store it somewhere in order to be able to reference it). It's only possible to return a reference to a str
if the enum is MyEnum::S
. And Deref
must never fail. Using AsRef
has the same problem, and both should not involve complex operations but be "cheap".
So the only alternative seems to be From
(and Into
, which is related, but you should ideally implement From
). By allowing conversion from a reference and returning a Cow
, you can avoid unnecessary cloning in case the enum was MyEnum::S
.
Note: Cow<'a, str>
already implements Deref<Target=str>
for you.
But I believe what's best really depends on your use case / scenario.
You could also use a method that returns the Cow
without involving From
and Into
at all:
use std::borrow::Cow;
enum MyEnum{
F(f64),
I(i64),
S(String)
}
impl MyEnum {
fn cow_str(&self) -> Cow<'_, str> {
match self {
MyEnum::F(x) => Cow::Owned(x.to_string()),
MyEnum::I(x) => Cow::Owned(x.to_string()),
MyEnum::S(x) => Cow::Borrowed(&x),
}
}
}
fn main(){
let a = MyEnum::F(1.1);
let b = MyEnum::I(100);
let c = MyEnum::S(String::from("hello world"));
println!("{}", a.cow_str());
println!("{}", b.cow_str());
println!("{}", c.cow_str());
}
(Playground)
Oh, and speaking of different use cases:
If you're just interested in Debug
output, you could also simply do:
#[derive(Debug)]
enum MyEnum{
F(f64),
I(i64),
S(String)
}
fn main(){
let a = MyEnum::F(1.1);
let b = MyEnum::I(100);
let c = MyEnum::S(String::from("hello world"));
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
}
(Playground)
Note the {:?}
in the formatting string.