6

I have a type Instruction and would use std::convert::TryFrom to convert from a string.

Shall I implement over String or &str? If I use &str I am obliged to use &* pattern or as_ref().

I have something like: Rust Playground permalink

use std::convert::TryFrom;
enum Instruction {
    Forward, /* other removed for brievity */
}
#[derive(Debug)]
struct InstructionParseError(char);
impl std::convert::TryFrom<&str> for Instruction {
    type Error = InstructionParseError;    
    fn try_from(input: &str) -> Result<Self, Self::Error> {
      match input {
        "F" => Ok(Instruction::Forward),
        _ => unimplemented!(), // For brievity
      }
    }
}

fn main() {
    // I use a string because this input can come from stdio.
    let instr = String::from("F");
    let instr = Instruction::try_from(&*instr);
}

I read this answer: Should Rust implementations of From/TryFrom target references or values? but i am wondering what is the best option: Implement both? Use impl avanced typing?

Darnuria
  • 166
  • 1
  • 10
  • I think you should only implement it for `&str` but you could add an impl for `&String` but that not really important your type can be construct from &str, any struct other than String could have a str representation and you can't impl TryFrom for every type, let the user do the transformation to str should be ok – Stargateur Oct 17 '20 at 20:57
  • Yes but I will force user to use `&*val` or `val.as_ref()` if this user pass a `val: String`, alas the compiler message is terrible... Would avoid a bad developer experience in this case, if there is an idiomatic way it would be perfect. – Darnuria Oct 17 '20 at 21:52

2 Answers2

2

One solution I think after reading @SirDarius' comment is just to implement also for String and use as_ref() or &* inside.

use std::convert::TryFrom;
enum Instruction {
    Forward, /* other removed for brievity */
}
#[derive(Debug)]
struct InstructionParseError(char);
impl std::convert::TryFrom<String> for Instruction {
    type Error = InstructionParseError;    
    fn try_from(input: String) -> Result<Self, Self::Error> {
        Instruction::try_from(input.as_ref())
    }
}

Like described here Should Rust implementations of From/TryFrom target references or values? maybe in the future if some change is made with blanket implemation, AsRef will be usable.

SirDarius
  • 41,440
  • 8
  • 86
  • 100
Darnuria
  • 166
  • 1
  • 10
  • @SirDarius: Oh thanks for your correction! I assumed the usage was like on github/gitlab etc. Didn't saw the @ transformed into a link so I edited :) – Darnuria Oct 17 '20 at 22:33
-2

*** This doesn't actually work as SirDarius points out below.

Use T: AsRef<str>.

impl<T: AsRef<str>> std::convert::TryFrom<T> for Instruction {
    type Error = InstructionParseError;    
    fn try_from(input: T) -> Result<Self, Self::Error> {
      let input: &str = input.as_ref();
      match input {
        "F" => Ok(Instruction::Forward),
        _ => unimplemented!(), // For brievity
     }
    }
}
NovaDenizen
  • 5,089
  • 14
  • 28
  • 1
    This would be a pretty idiomatic solution, except that it conflicts with the blanket implementation from `core` for TryFrom: `impl TryFrom for T where U: Into;`and therefore does not compile. – SirDarius Oct 17 '20 at 22:20
  • Dang it, I always run into that. – NovaDenizen Oct 17 '20 at 22:22
  • It's forbidden due to orphan rule ? Just read this [issue](https://stackoverflow.com/questions/45126120/if-intostring-is-not-implemented-for-string-why-are-these-implementation?rq=1) and read again [ch10-02 trait](https://doc.rust-lang.org/book/ch10-02-traits.html). – Darnuria Oct 17 '20 at 22:42