1

I am converting a variety of types to String when they are passed to a function. I'm not concerned about performance as much as ergonomics, so I want the conversion to be implicit. The original, less generic implementation of the function simply used &[impl Into<String>], but I think that it should be possible to pass a variety of types at once without manually converting each to a string.

The key is that ideally, all of the following cases should be valid calls to my function:

// String literals
perform_tasks(&["Hello", "world"]);
// Owned strings
perform_tasks(&[String::from("foo"), String::from("bar")]);
// Non-string types
perform_tasks(&[1,2,3]);
// A mix of any of them
perform_tasks(&["All", 3, String::from("types!")]);

Some various signatures I've attempted to use:

fn perform_tasks(items: &[impl Into<String>])

The original version fails twice; it can't handle numeric types without manual conversion, and it requires all of the arguments to be the same type.

fn perform_tasks(items: &[impl ToString])

This is slightly closer, but it still requires all of the arguments to be of one type.

fn perform_tasks(items: &[&dyn ToString])

Doing it this way is almost enough, but it won't compile unless I manually add a borrow on each argument.

And that's where we are. I suspect that either Borrow or AsRef will be involved in a solution, but I haven't found a way to get them to handle this situation. For convenience, here is a playground link to the final signature in use (without the needed references for it to compile), alongside the various tests.

Zombie_Pigdragon
  • 304
  • 4
  • 13
  • You can't have an array of mixed types, at least without indirection. – Colonel Thirty Two Nov 15 '21 at 22:51
  • 1
    The issue I see is that you need to borrow before you can create the slice, so any solution with a "normal" function, to me, seems like it wouldn't work without the explicit borrow. Maybe this is a place for a macro? – cadolphs Nov 15 '21 at 22:54
  • 1
    "A mix of any of them" is not valid Rust, Rust arrays are homogeneous. You'll have to give that one up, or use a macro. – user4815162342 Nov 15 '21 at 23:03

2 Answers2

1

The following way works for the first three cases if I understand your intention correctly.

pub fn perform_tasks<I, A>(values: I) -> Vec<String> 
where
    A: ToString,
    I: IntoIterator<Item = A>,
{
    values.into_iter().map(|s| s.to_string()).collect()
}

As the other comments pointed out, Rust does not support an array of mixed types. However, you can do one extra step to convert them into a &[&dyn fmt::Display] and then call the same function perform_tasks to get their strings.

let slice: &[&dyn std::fmt::Display] = &[&"All", &3, &String::from("types!")];
perform_tasks(slice);

Here is the playground.

Joe_Jingyu
  • 1,024
  • 6
  • 9
-1

If I understand your intention right, what you want is like this

fn main() {
    let a = 1;
    myfn(a);
}

fn myfn(i: &dyn SomeTrait) {
   //do something
}

So it's like implicitly borrow an object as function argument. However, Rust won't let you to implicitly borrow some objects since borrowing is quite an important safety measure in rust and & can help other programmers quickly identified which is a reference and which is not. Thus Rust is designed to enforce the & to avoid confusion.

whilrun
  • 1,701
  • 11
  • 21
  • This does not answer the question; the question specifically asks about slices, while this answer only covers references to single values. – Zombie_Pigdragon Jul 16 '23 at 22:59