Trait bound for type that can be dereferenced into an iterator of items that can be referenced as str

I am trying and failing to write a trait bound that requires a type to be be deref-able into an iterator that yields items that can be referenced to str:

use std::ops::Deref;

fn main() {
    let strings = vec!["hello", "world"];
    test(strings);
}

fn test<T>(t: T)
where
    T: Deref,
    for<'a> &'a T::Target: IntoIterator,
    for<'a> <&'a <T as Deref>::Target as IntoIterator>::Item: AsRef<str>,
{
}

Can somebody help me out here?
Why does the above code not work?

It's possible enough to make the type signatures work, here, but one issue that you're going to run into no matter what is that there's a conflict between Deref and IntoIterator. Deref allows an &T to be converted implicitly into an &<T as Deref>::Target, while IntoIerator provides a single method into_iterator(self) — note the lack of &. IntoIterator consumes its input, and Deref doesn't provide you owned access to whatever type you're dereferencing to.

That said, however, from a type-system-only perspective this will compile (though it cannot be executed for the reasons above):

use std::ops::Deref;

fn main() {
    let strings = vec!["hello", "world"];
    test(&strings);
}

fn test<T, I>(t: T)
where
    T: Deref,
    T::Target: IntoIterator<Item = I>,
    I: AsRef<str>,
{
}

Current limitations of the trait solver; this issue or a similar one.

Sometimes an intermediate trait can help.

trait DerefIter {
    type Output<'a>: IntoIterator<Item: AsRef<str>> where Self: 'a;
    fn deref_iter(&self) -> Self::Output<'_>;
}

impl<C> DerefIter for C
where
    C: Deref,
    for<'a> &'a C::Target: IntoIterator<Item: AsRef<str>>,
{
    type Output<'a> = &'a <Self as Deref>::Target where Self: 'a;
    fn deref_iter(&self) -> Self::Output<'_> {
        &**self
    }
}

fn test<T: DerefIter>(t: T) {
}
2 Likes

Thank you. This brought me to the solution I really needed.
I did not need Deref at all, since it is enough to work with references for me:

use std::borrow::Cow;
use std::collections::HashSet;

fn main() {
    let vec_str = vec!["hello", "world"];
    test(vec_str);
    let array_string = ["hello".to_string(), "world".to_string()];
    test(array_string);
    let hash_set_cow: HashSet<Cow<'static, str>> = vec!["hello".into(), "world".to_string().into()]
        .into_iter()
        .collect();
    test(hash_set_cow);
}

fn test<T>(t: T)
where
    for<'a> &'a T: StrRefIter,
{
    for item in (&t).str_ref_iter() {
        println!("{}", item.as_ref());
    }
}

trait StrRefIter {
    type Output: Iterator<Item: AsRef<str>>;
    fn str_ref_iter(self) -> Self::Output;
}

impl<T> StrRefIter for T
where
    T: IntoIterator<Item: AsRef<str>>,
{
    type Output = <Self as IntoIterator>::IntoIter;
    fn str_ref_iter(self) -> Self::Output {
        self.into_iter()
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.