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).
CodePudding user response:
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?