Confusing type annotation error

Hi all,
New to Rust, figured I'd create a game of tic-tac-toe for a simple first project.
However, I'm having difficulty with converting an enum to a char idiomatically.
It is my understanding that I should use try_into() for this, however it proves difficult to convert from CellValue into a char.

The error is found in the match statement: self.try_into() requires a type annotation, but complains when I use a turbo fish operator claiming that I have "Wrong number of type arguments: expected 0, found 1"

Code
use std::fmt;
use std::fmt::Formatter;

#[derive(Debug, Default)]
pub enum CellValue {
    #[default] Empty,
    Naught,
    Cross
}

impl fmt::Display for CellValue {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self.try_into() {
            Ok(variant) => write!(f, "{variant}"),
            Err(err) => err
        }
    }
}

impl TryFrom<char> for CellValue {
    type Error = String;

    fn try_from(value: char) -> Result<Self, Self::Error> {
        match value {
            ' ' => Ok(Self::Empty),
            'o' => Ok(Self::Naught),
            'x' => Ok(Self::Cross),
            _   => Err("Invalid char".to_string())
        }
    }
}

Any help would be appreciated. Sorry if this has been answered before - it's 5am and I'm not really interested in scouring the forum for similar posts to this. :sweat_smile:

There are a few issues.

First, you implement TryFrom<char> for CellValue. This represents a char → CellValue conversion. But self.try_into() is a CellValue → ? conversion. You're trying to use a recipe for baking a cake to un-bake a cake.

You want to implement a CellValue → char conversion. Because this cannot fail (there is a result for all possible values of CellValue), you want to impl From<CellValue> for char or impl Into<char> for CellValue.

The next problem is that Display takes &self: that is, the conversion needs to work with a reference. You haven't marked CellValue as Clone or Copy, so this doesn't work directly with the From/Into traits. You can either implement the conversions for &CellValue instead, or make the type copyable. Given how simple it is, I've gone with just making it trivially copyable. To ensure the compiler can work out the type you want to convert to, I've used char::from as the invocation. You could also do something like let variant: char = (*self).into(); (implementing From automatically gives you an implementation of Into; the reverse is not true).

As a final adjustment, I collapsed the two use items at the top into one for brevity. Here's a playpen with the modified code:

use std::fmt::{self, Formatter};

#[derive(Clone, Copy, Debug, Default)]
pub enum CellValue {
    #[default] Empty,
    Naught,
    Cross
}

impl fmt::Display for CellValue {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", char::from(*self))
    }
}

impl From<CellValue> for char {
    fn from(value: CellValue) -> char {
        match value {
            CellValue::Empty => ' ',
            CellValue::Naught => 'o',
            CellValue::Cross => 'x',
        }
    }
}
3 Likes

You could keep the impl From<char> for CellValue

It might be useful elsewhere. For testing, if nothing else :slight_smile:

mod test {
    #[test]
    fn test_diagonal_win() {
        let grid = Grid::from("\
x o
 x 
o x";
        // Check that X player wins somehow.
    }
}

I'm imagining a Grid struct here that contains CellValues, it would use CellValue's From implementation in its own From<&'static str> (or maybe you'd need to do From<String>?) implementation.

Being able to quickly write ASCII diagrams to construct a whole grid is much nicer than messing around constructing things in code.

Reminds me (somehow) of an excellent article about testing: How to Test

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.