1

I've been doing the Advent of Code to learn Rust, and on day 3 I ended up writing this:

fn ones_per_bit(lines:&Vec<String>, bitcount:usize) -> Vec<u32> {
  let mut out: Vec<u32> = Vec::new();
  out.resize(bitcount, 0);
  for line in lines {
    for (i, c) in line.chars().enumerate() {
      if c == '1' { out[i] += 1; }
    }
  }
  return out;
}

And then copied/pasted it so that I can call it with a &Vec<&String> instead of a &Vec<String>. This worked, but to avoid duplicating the function body, I was hoping to use generics, but my naive implementation looked like this:

fn ones_per_bit<T>(lines:&Vec<T>, bitcount:usize) -> Vec<u32> {
  let mut out: Vec<u32> = Vec::new();
  out.resize(bitcount, 0);
  for line in lines {
    for (i, c) in line.chars().enumerate() {
      if c == '1' { out[i] += 1; }
    }
  }
  return out;
}

Which resulted in:

error[E0599]: no method named `chars` found for reference `&T` in the current scope
  --> main.rs:44:24
   |
44 |     for (i, c) in line.chars().enumerate() {
   |                        ^^^^^ method not found in `&T`

So I'm missing a concept here. I seem to need to tell rust what types of methods to expect on the generic type, but I'm not sure the best way to do that (or if there's a better language feature I should be using here).

brakeley
  • 192
  • 7

1 Answers1

3

What you want here is a type that you can dereference as &str.

This is usually done with a <T: AsRef<str>> generic constraint:

fn ones_per_bit<T: AsRef<str>>(lines: &[T], bitcount:usize) -> Vec<u32> {
  let mut out: Vec<u32> = Vec::new();
  out.resize(bitcount, 0);
  for line in lines {
    for (i, c) in line.as_ref().chars().enumerate() {
      if c == '1' { out[i] += 1; }
    }
  }
  return out;
}

Side note: declare the argument as &[T] rather than &Vec[T] so that it works for bot vec references and slices thanks to deref coercion. See Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • That worked great, thanks! So `Vec` is specifically the generic Vec type, but what does `[T]` mean to the compiler? Does it just mean "slice", and then the compiler implicitly casts Vecs and Vec refs to slices? – brakeley Dec 23 '21 at 16:46
  • 1
    @AngelicosPhosphoros Yes, I was the one who wrote a comment a little too fast and wrote AsRef instead of Deref. I've since moved the main content of my comment (the link to the book on deref coercion) to the answer. Your comment was right and useful. – Denys Séguret Dec 24 '21 at 06:22