Writing functions and traits that work on Iterables

How can I structure my traits and impls to correspond to the following C# function signatures:

IEnumerable<int> AsLd(string text)
public int Calc(IEnumerble<int> nums) 

I successfully wrote a function to convert a string to an enumerable of numbers (I dumbed down the code to simply it):

    pub(crate) trait LetterDigestUtils{
        fn as_ld<'a>(&'a self) -> Box<dyn Iterator<Item=i32> + 'a>;
    }

    impl LetterDigestUtils for str {
        fn as_ld<'a>(&'a self) -> Box<dyn Iterator<Item=i32> + 'a> {
           Box::new( self.chars().into_iter().map(|x| x))
        }
    }

I'm trying to do something like:

    pub(crate) trait CalcUtils {
        fn calc(&self) -> i32;
    }

    impl<'a> CalcUtils for &mut dyn Iterator<Item=i32> {
        fn calc(&self) -> i32 {
            calculate(*self)
        }
    }

    impl CalcUtils for str {
        fn calc(&self) -> i32 {
           self.as_ld().calc()
        }
    }

However I'm getting an error:

    error[E0599]: no method named `calc` found for struct `std::boxed::Box<dyn std::iter::Iterator<Item = i32>>` in the current scope
       --> src/Calc.rs:107:21
        |
    107 |        self.as_ld().calc()
        |                     ^^^^ method not found in `std::boxed::Box<dyn std::iter::Iterator<Item = i32>>`
        |
        = help: items from traits can only be used if the trait is implemented and in scope
        = note: the following trait defines an item `calc`, perhaps you need to implement it:
                candidate #1: `Calc::CalcUtils`

I believe the issue is that iterators need to be mutable so I needed to add &mut to this signature: impl<'a> CalcUtils for &mut dyn Iterator<Item=i32>. and the as_ld trait doesn't return that type.
However I tried making as_ld return a mutable Box and I got this error:

       = note: expected mutable reference `&'a mut std::boxed::Box<(dyn std::iter::Iterator<Item = i32> + 'a)>`
                             found struct `std::boxed::Box<std::iter::Map<std::str::Chars<'_>, [closure@src/LDTypes.rs:47:47: 47:110]>>`

Thanks

Meta, but please surround code blocks with ```:

```
code goes here
```

You probably want this:

pub(crate) trait CalcUtils {
    fn calc(&self) -> i32;
}

impl<'a, T: Iterator<Item=i32>> CalcUtils for T {
    fn calc(self) -> i32 {
        calculate(self)
    }
}

Yes, thanks - that makes sense, however it doesn't play well with my other trait that converts str to an iterator of, for simplicities sake, i32. Since I wanted to write an "extension" to string to convert it to an iterable of i32, I wrote a trait. But impl Trait is not allowed in return position on a trait, so I ned to us a boxed dyn trait.


pub(crate) trait LetterDigestUtils{
    fn as_ld<'a>(&'a self) -> Box<dyn Iterator<Item=i32> + 'a>;
}

impl LetterDigestUtils for str {
    fn as_ld<'a>(&'a self) -> Box<dyn Iterator<Item=i32> + 'a> {
       Box::new( self.chars().into_iter().map(|x| *CharToLdLookup.get(&x).unwrap_or(&0)))
    }
}

I am therefore receiving an error:

error[E0599]: no method named `calc` found for struct `std::boxed::Box<dyn std::iter::Iterator<Item = i32>>` in the current scope

Do I need to convert the string to a Vec in order to get this to work? Or can this be done?

Boxed iterators do indeed implemented the Iterator trait, see playground. Is the CalcUtil trait in scope?

You are right yet again. Thanks for helping me along.
I still haven't gotten to the bottom of this:

Trait

impl<T: Iterator<Item=LDCode>> CalcUtils for T {
 ...
    fn calc_with(&self, lookup: &Lookup) -> i32 {
        self.fold(0, |acc, x| {
            let mut ret = acc;
            if let LDCode::Letter(i) = x {
                ret += lookup[i as usize]
            }
            ret
        })
    }
...

Error:

error[E0507]: cannot move out of `*self` which is behind a shared reference
  --> src/Calc.rs:78:9
   |
78 |         self.fold(0, |acc, x| {
   |         ^^^^ move occurs because `*self` has type `T`, which does not implement the `Copy` trait

error: aborting due to previous error

You need to take ownership of the iterator to use it for anything, so make that method take self instead of &self.

That makes sense. However now my str impl is giving me issues since it is !Sized

error[E0277]: the size for values of type `str` cannot be known at compilation time
  --> src/Calc.rs:97:13
   |
97 |     fn calc(self) -> i32 {
   |             ^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `str`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
   = note: all local variables must have a statically known size
   = help: unsized locals are gated as an unstable feature

Here is the impl:


impl CalcUtils for str {
 ...
    fn calc_with(self, lookup: &Lookup) -> i32 {
       self.as_ld().calc_with(lookup)
    }
...
}

You should probably implement the trait for &str instead then.

Wow the program works!! Thanks a million.
What happened was that I changed it to &str but I got a conflicting impl error, so I realised that I simply did not need any impl specifically for &str, and I could just use .as_ld().calc() at the call site, which I did and it worked.

1 Like

I noticed that the impl as we defined it:

impl<T: Iterator<Item=LDCode>> CalcUtils for T 

only works on Vec/slices it I do ld.into_iter().calc() - ie. only if I move the iterator. Using .iter() gives me the following error:

error[E0599]: no method named `calc` found for struct `std::slice::Iter<'_, LDTypes::LDCode>` in the current scope
  --> src/main.rs:46:34
   |
46 |             let val =  ld.iter().calc();
   |                                  ^^^^ method not found in `std::slice::Iter<'_, LDTypes::LDCode>`
   |

How can I define the impl that it works for .iter() (borrowing)?
I tried this:

impl<'a, T: Iterator<Item=LDCode>> CalcUtils for T {

and it gave me the same error.

Can you please explain to where I'm going wrong?
My goal is for the impl to work on both other iter chains as well as vecs/slices - the trait itself doesn't need ownership to do its job.

You need to use references as the item type in the Iterator.

I tried both of the following (stringing through the related changes in the code) and I landed up with the same error:

Attempt 1
impl<'a, T: Iterator<Item=&'a LDCode>> CalcUtils for T {
Attempt 2
impl<'a, T: Iterator<Item=&'a LDCode> + 'a> CalcUtils for T {
error[E0599]: no method named `calc` found for struct `std::slice::Iter<'_, &LDTypes::LDCode>` in the current scope
  --> src/main.rs:46:34
   |
46 |             let val =  ld.iter().calc();
   |                                  ^^^^ method not found in `std::slice::Iter<'_, &LDTypes::LDCode>`

Did I misunderstand what you meant by "use references as the item type in the Iterator."?

The first version should work. Perhaps you haven't imported the CalcUtils traits? Please see this playground.

Thank you - I managed to create a repro:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9d00df1dc474ca4ef7a24f35109bc2c2

@Guardian spirit pointed out the issue on discord:

b.iter().calc(); doesn't work
Well, because you get an iterator over &&LDCode .

so the solution was to do

 let val = b.iter().map(|l|*l).calc();

Which seems a bit awkward to me.

I just found out that

.copied()

is the equivalent of

.map(|l|*l)
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.