
use std::convert::{
    TryInto,
    TryFrom,
};

type BoxError = Box<dyn std::error::Error>;
type XResult<T> = Result<T, BoxError>;

fn do_print<T>(t: T) -> XResult<()> where T: TryInto<String>, T::Error: Into<BoxError> {
    println!("--> {}", t.try_into().map_err(Into::into)?);
    Ok(())
}

fn do_print2<T>(t: T) where T: Into<String> {
    println!("==> {}", t.into())
}

struct StructX {
    name: String,
}

struct StructY {
    name: String,
}

// auto have: TryInto<String> for StructX
impl TryFrom<StructX> for String {
    type Error = BoxError;

    fn try_from(sx: StructX) -> XResult<Self> {
        Ok(format!("NAME: {}", sx.name))
    }
}

impl From<StructY> for String {

    fn from(sx: StructY) -> Self {
        format!("NAME:: {}", sx.name)
    }
}

fn main() -> XResult<()> {
    let x = StructX {
        name: "hater".to_owned(),
    };
    do_print(x)?;
    let y = StructY {
        name: "hater".to_owned(),
    };
    do_print2(y);
    Ok(())
}
