1

I am trying to understand generics in Rust. I am not able to get what went wrong here

fn say<T>(msg: &T)  {
    let slen = msg.chars().count();
    if slen > 0 {
        println!("Char Count {} " ,slen);
    } 
}


fn main() {
    let msg = String::from("Hello World from Rust!!!");
    say::<String>(&msg);
}

Compilation Error:

error[E0599]: no method named `chars` found for reference `&T` in the current scope
 --> hell.rs:4:20
  |
4 |     let slen = msg.chars().count();
  |                    ^^^^^ method not found in `&T`
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Galactus
  • 87
  • 1
  • 8
  • 1
    `say` means that "for whatever `T`, this method will work." Arbitrary types aren't guaranteed to have a `chars` method, so the generic doesn't work here. – Aplet123 Mar 25 '21 at 13:51
  • Thanks a lot for your response. how to make this work.also isnt hinting of while invoking function wont help ? – Galactus Mar 25 '21 at 13:52
  • 1
    If your generic only works with a specific type, then it won't work. Just don't use a generic and take in `&str` instead. – Aplet123 Mar 25 '21 at 13:54
  • Related: https://stackoverflow.com/q/53085270 – E_net4 Mar 25 '21 at 14:23

2 Answers2

2

The idea behind Rust generics is that the generic types are both restricted and defined by trait bounds. A function generic over a T bound by a set of traits may assume that T has methods from those traits and no more. Since T is not bound by any trait, there is no T::chars() method to call.

To write generic code that invokes methods not provided by any existing traits, such as chars(), you can write your own trait that provides the needed functionality, and implement it for the types you need. For example:

trait CharCount {
    fn char_count(&self) -> usize;
}

impl CharCount for String {
    fn char_count(&self) -> usize {
        self.chars().count()
    }
}

With this trait you can write your generic method to make use of it:

fn say<T: CharCount>(msg: &T)  {
    let slen = msg.char_count();
    if slen > 0 {
        println!("Char count: {}", slen);
    } 
}

You can invoke say with String, but also with any other type that decides to implement the CharCount trait, even if it's a type you haven't envisioned while writing the trait.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • its great coincidence by the time this answer came i too came to find similar solution. Accepting your solution as answer ```trait Dummy { fn len(&self) -> usize; } impl Dummy for String { fn len(&self) -> usize { return self.chars().count(); } } fn say(msg: T) { let slen = msg.len(); if slen > 0 { println!("Char Count {} " ,slen); } } fn main() { let msg=String::from("Hello World from Rust!!!"); say::(msg); }``` – Galactus Mar 25 '21 at 14:29
2

Your function say assumes that the input has a chars method that creates an iterator over chars. There aren't many types that this would work for, so it doesn't seem very useful to make it generic.

The main two types that have a chars iterator method are String and &str. If you change the parameter to be &str then you can accept either one of them:

fn say(msg: &str)  {
    let slen = msg.chars().count();
    if slen > 0 {
        println!("Char Count {} ", slen);
    } 
}


fn main() {
    let msg: String = String::from("Hello World from Rust!!!");
    say(&msg);
    let msg: &str = "Hello World from Rust!!!";
    say(msg);
}

See also:

Peter Hall
  • 53,120
  • 14
  • 139
  • 204