Passing AsRef<str> parameters to function

Hello everyone.

I spent quite a lot of time trying to figure the proper way to do this, and have thus far failed. I'm pretty sure the solution to my issue is quite evident to anyone having a clearer picture of rust's memory model than I do, and hope someone could help me.

Here's some boiled-down code:

fn exec<T: AsRef<str>>(bin: T, args: &[T], dir: Option<T>) {
    // calling Command::new and so on
}

fn exec_ls<T: AsRef<str>>(args: &[T], dir: Option<T>) {
    // example bin, could be anything
    exec("ls", args, dir);
}

fn main() {
    exec_ls(&["-l"], None);
}

There's 2 main issues:

  1. the "mismatched type" error:
error[E0308]: mismatched types
 --> src/main.rs:7:16
  |
5 | fn exec_ls<T: AsRef<str>>(args: &[T], dir: Option<T>) {
  |            - this type parameter
6 |     // example bin, could be anything
7 |     exec("ls", args, dir);
  |                ^^^^ expected `&str`, found type parameter `T`
  |
  = note: expected reference `&[&str]`
             found reference `&[T]`

error[E0308]: mismatched types
 --> src/main.rs:7:22
  |
5 | fn exec_ls<T: AsRef<str>>(args: &[T], dir: Option<T>) {
  |            - this type parameter
6 |     // example bin, could be anything
7 |     exec("ls", args, dir);
  |                      ^^^ expected `&str`, found type parameter `T`
  |
  = note: expected enum `std::option::Option<&str>`
             found enum `std::option::Option<T>`
  1. error[E0277]: the size for values of type str cannot be known at compilation time on the call to exec l.7: exec("ls", args, dir); I could apply the suggestion to make T + ?Sized, but since it requires to change the type to a reference, I'm not sure how that would work as a whole, as AsRef is already a reference, if i understand correctly.

I feel this is a great learning opportunity, but i'm starting to be a bit lost, and can't seem to fix this. I fear that the more i look into the issue, the more i'm confused, and humbly request assistance in the matter.

Thank you for your time and please forgive the possible english errors.

paul

By using "ls" as the first argument in the call of exec(), you've made the compiler infer the exec's generic type T as &str, so it expects that args are &[&str] and dir is Option<&str> (I'm leaving the lifetimes out of this), but the rest of the arguments in the call are still generic and you get the mismatch. Declaring exec() like this:

fn exec<S: AsRef<str>, T: AsRef<str>>(bin: S, args: &[T], dir: Option<T>) { ...

...will make the minimal example compile (with no guarantees that the full code will).

2 Likes

Hello, @inejge, thank you for your response. Indeed, your solution works, the following code compiles:

fn exec<T, U, V>(bin: T, args: &[U], dir: Option<V>) 
where
    T: AsRef<str>,
    U: AsRef<str>,
    V: AsRef<str>
{
    // calling Command::new and so on
}

fn exec_ls<T: AsRef<str>, U: AsRef<str>>(args: &[T], dir: Option<U>) {
    // example bin, could be anything
    exec("ls", args, dir);
}

fn main() {
    exec_ls(&["-l"] as &[&str], None as Option<&str>);
}

This seems quite verbose, and don't understand why the compiler asked me to specify the type of the &["-l"] in the exec_ls call.

On the "real" project, i'm still having the error[E0277]: the size for values of type str cannot be known at compilation time on the equivalent to exec. No real way of knowing which parameter is causing issues, but i will do some tests by myself.

So again, thank you for your kind answer.

Is there a standard way to make function accept textual data? I thought i had it right with the AsRef<str> generic parameters, but it doesn't feel practical...

paul

For individual parameters, there's not much benefit of using a generic instead of &str directly: Auto-deref rules will often make this trivial for the caller to provide. AsRef is mostly useful for things like slices and iterators; you can use the impl Trait shortand here. I would probably write your example like this:

fn exec(bin: &str, args: &[impl AsRef<str>], dir: Option<&str>) 
{
    // calling Command::new and so on
}

fn exec_ls(args: &[impl AsRef<str>], dir: Option<&str>) {
    // example bin, could be anything
    exec("ls", args, dir);
}

fn main() {
    exec_ls(&["-l"], None);
}
1 Like

Thank you to both of you. The original code compiles & seems to do what it's intended to.

I'm now closing this subject.