Use From<Into<String>> to ease usage of nested structs


#1

Hi all

I would like to ease the use of the telegram-bot crate with the following addition:

impl From<S> for KeyboardButton where S: Into<String> {
    fn from<S>(text: S) -> Self {
        KeyboardButton::new(text)
    }
}

but I keep getting errors from the compiler, when I use the following code:

let v : Vec<KeyboardButton> = vec![ "A" ];

this however works:

let v : Vec<KeyboardButton> = vec![ KeyboardButton::new("A"), "B".into(), String::new("C") ];

I can implement it like this, but I would like to reduce the code:

impl From<&'static str> for KeyboardButton {
    fn from(text: &'static str) -> Self {
        KeyboardButton::new(text)
    }
}
impl From<String> for KeyboardButton {
    fn from(text: String) -> Self {
        KeyboardButton::new(text)
    }
}

The rest of the code:

#[derive(RustcDecodable, Debug, PartialEq, Clone)]
pub struct KeyboardButton {
    pub text: String,
    pub request_contact: Option<bool>,
    pub request_location: Option<bool>,
}

impl KeyboardButton {
    fn new<S>(text : S) -> Self where S: Into<String> {
        KeyboardButton {
            text: text.into(),
            request_contact: None,
            request_location: None,
        }
    }
}

#2

It doesn’t work because constructing a Vec has nothing to do with calling From whatsoever. They’re totally unrelated.


#3

Okay, do you have a hint for the code dupplication problem?


#4

Just do the call to from yourself:

macro_rules! vec_from {
    ($($es:expr),*) => {
        vec![$(::std::convert::From::from($es)),*]
    };
}

struct Cee;
impl From<Cee> for String {
    fn from(_: Cee) -> String {
        "C".to_owned()
    }
}

fn main() {
    let v: Vec<String> = vec_from!["A", "B", Cee];
    println!("{:?}", v);
}

#5

Ah, this is very cool :smile:

Do you have a hint how to combine the two From impls into one?

impl From<&'static str> for KeyboardButton { ... }
impl From<String> for KeyboardButton { ... }

is there a good syntax to do this? Can I use From<Into<String>>?


#6

Hang on, what was the problem with what you initially wrote? You said it worked. I tried a similar case and it worked fine. If you’ve got a piece of code that doesn’t work, you should post that code (all of it, so people can throw it into the playpen and work on it), and the actual compiler output.


#7

I see, it is a bit confusing with the edits.

The first problem I had, was the activation of the From<_> trait in Vec, which your macro solved. Thanks again :smile:

The second problem I still have is the double code.
Right now it works with the code like this:

#[derive(RustcDecodable, Debug, PartialEq, Clone)]
pub struct KeyboardButton {
    pub text: String,
    pub request_contact: Option<bool>,
    pub request_location: Option<bool>,
}

impl Default for KeyboardButton {
    fn default() -> Self {
        KeyboardButton {
            text: "".into(),
            request_contact: None,
            request_location: None,
        }
    }
}
impl From<&'static str> for KeyboardButton {
    fn from(text: &'static str) -> Self {
        KeyboardButton::new(text, None, None)
    }
}
impl From<String> for KeyboardButton {
    fn from(text: String) -> Self {
        KeyboardButton::new(text, None, None)
    }
}
impl KeyboardButton {
    pub fn new<S>(text : S, request_contact: Option<bool>, request_location: Option<bool
    >) -> Self where S: Into<String> {
        KeyboardButton {
            text: text.into(),
            request_contact: request_contact,
            request_location: request_location,
        }
    }
}

now I would like to reduce the two From<_> implementations to one. Maybe something like this:

impl From<Into<String>> for KeyboardButton {
    fn from(text: Into<String>) -> Self {
        KeyboardButton::new(text, None, None)
    }
}

but I get a compiler error:

/types/mod.rs:711:6: 711:24 error: the trait `core::marker::Sized` is not implemented for the type `core::convert::Into<collections::string::String> + 'static` [E0277]
src/types/mod.rs:711 impl From<Into<String>> for KeyboardButton {
                          ^~~~~~~~~~~~~~~~~~
src/types/mod.rs:711:6: 711:24 help: run `rustc --explain E0277` to see a detailed explanation
src/types/mod.rs:711:6: 711:24 note: `core::convert::Into<collections::string::String> + 'static` does not have a constant size known at compile-time
src/types/mod.rs:711:6: 711:24 note: required by `core::convert::From`
src/types/mod.rs:711:6: 711:24 error: the trait `core::convert::Into` cannot be made into an object [E0038]
src/types/mod.rs:711 impl From<Into<String>> for KeyboardButton {
                          ^~~~~~~~~~~~~~~~~~
src/types/mod.rs:711:6: 711:24 help: run `rustc --explain E0038` to see a detailed explanation
src/types/mod.rs:711:6: 711:24 note: the trait cannot require that `Self : Sized`
src/types/mod.rs:711:6: 711:24 error: the trait `core::marker::Sized` is not implemented for the type `core::convert::Into<collections::string::String> + 'static` [E0277]
src/types/mod.rs:711 impl From<Into<String>> for KeyboardButton {
                          ^~~~~~~~~~~~~~~~~~
src/types/mod.rs:711:6: 711:24 help: run `rustc --explain E0277` to see a detailed explanation
src/types/mod.rs:711:6: 711:24 note: `core::convert::Into<collections::string::String> + 'static` does not have a constant size known at compile-time
src/types/mod.rs:711:6: 711:24 note: required by `core::convert::From`
src/types/mod.rs:711:6: 711:24 error: the trait `core::convert::Into` cannot be made into an object [E0038]
src/types/mod.rs:711 impl From<Into<String>> for KeyboardButton {
                          ^~~~~~~~~~~~~~~~~~
src/types/mod.rs:711:6: 711:24 help: run `rustc --explain E0038` to see a detailed explanation
src/types/mod.rs:711:6: 711:24 note: the trait cannot require that `Self : Sized`
error: aborting due to 2 previous errors
Build failed, waiting for other jobs to finish...
error: aborting due to 2 previous errors
Could not compile `telegram-bot`.

The current source ist on github.


#8

First, reduce the code to just what’s needed to demonstrate the problem and compile.

struct KeyboardButton {
    pub text: String,
}

impl From<&'static str> for KeyboardButton {
    fn from(text: &'static str) -> Self {
        KeyboardButton::new(text)
    }
}

impl From<String> for KeyboardButton {
    fn from(text: String) -> Self {
        KeyboardButton::new(text)
    }
}

impl KeyboardButton {
    pub fn new<S>(text: S) -> Self where S: Into<String> {
        KeyboardButton {
            text: text.into(),
        }
    }
}

Next, copy+paste the first block of code you have, but fix the compile errors: you never introduced the S type, and from isn’t generic. I hadn’t noticed before because you were complaining about vec! not invoking From, not that the generic impl didn’t compile.

Add some invocations to make sure it works, and:

struct KeyboardButton {
    pub text: String,
}

impl KeyboardButton {
    pub fn new<S>(text: S) -> Self where S: Into<String> {
        KeyboardButton {
            text: text.into(),
        }
    }
}

impl<S> From<S> for KeyboardButton where S: Into<String> {
    fn from(text: S) -> Self {
        KeyboardButton::new(text)
    }
}

fn main() {
    let _: KeyboardButton = From::from("A");
    let _: KeyboardButton = From::from(String::from("B"));
}