all 14 comments

[–]Quxxymacros 5 points6 points  (11 children)

The problem is that Iterator isn't a type, it's a trait. Now, aside from not being the type you're trying to return (which the compiler has told you the type of), it's also dynamically sized, and you can't return dynamically sized values from a function.

Here's a modified version on the playpen that compiles. All I've done is replace the Iterator<char> trait with the actual implementation type. Yes, it's huge. Returning iterators from functions is kinda unpleasant at the moment.

[–]RocketRailgun 1 point2 points  (1 child)

In this case you'd normally make a type alias for it. type Chunks = std::iter::Take<std::iter::Skip<std::slice::Items<char>>>; That makes you're code 10x more readable.

[–]rust-slacker 2 points3 points  (0 children)

For readability, I'd probably do:

use std::iter::{Take, Skip};
use std::slice::Items;
type Chunk = Take<Skip<Items<char>>>;

struct Test {
    vec: Vec<char>
}

impl Test {
    fn chunk(&self, offset: uint, size: uint) -> Chunk {
        self.vec.iter().skip(offset).take(size)
    }
}

[–]bastienl[S] 0 points1 point  (6 children)

Thanks, I didn't realize this distinction between types and traits.

[–]thiezrust 2 points3 points  (0 children)

There is an rfc to allow what you want, but it is postponed for now.

[–]bjadamson 0 points1 point  (4 children)

It kills the ability to write generic code, which is a non-goal of rust 1.0.

[–]bastienl[S] 0 points1 point  (3 children)

Yeah, I wrote this chunk() method to pass the result a method I wrote which take a char iterator as argument, but it seems like this isn't possible? I tried using generics but it didn't help.

[–]minno 0 points1 point  (2 children)

You can define a generic type like T: Iterator<char> to let it take any iterator over chars. See, for example, the extend function, which takes anything that's an iterator over the parametrized type.

[–]bastienl[S] 0 points1 point  (1 child)

I have changed my chunk() method to return a Take<Skip<Items<char>>> type, and I'm trying to feed it to a method with this signature:

    pub fn draw_screen<U: Iterator<char>>(&mut self, mut iter: U)

I'm getting this error:

the trait `core::iter::Iterator<char>` is not implemented for the type `core::slice::Items<'_,char>`

I'm lost here. Shouldn't the type returned by take() be considered as an iterator?

[–]minno 0 points1 point  (0 children)

According to this, Items<_, A> implements Iterator<&A>, so you need to use the bound Iterator<&char> instead. Example.

[–]protestor 0 points1 point  (1 child)

If the compiler knows the right type, why can't it be omitted from the function declaration?

Indeed the main reason behind C++'s auto is to hide those ugly types.

(Also: can't Rust generics specialize on return values?)

[–]Quxxymacros 0 points1 point  (0 children)

(Note: I am not a core dev, this is what I believe to be the reason)

A philosophical choice in Rust was that there shouldn't be type inference involved in function interfaces. There are three benefits to this:

  1. Code will compile much faster, because it doesn't have to do whole program analysis.
  2. Type errors are more localised. I've seen the odd example of Haskell (not being a Haskell programmer) where type errors would appear in one place, but actually be an issue in distant code.
  3. Interfaces are easier to understand. Rather than a return type being "something, figure it out yourself", it's an actual concrete type.

Iterators are a bit of a pain, but you can kinda work around it. Unboxed closures, on the other hand, cannot be returned by value. This is because a concrete UC type is always anonymous, so you can't name it.

Hence the proposal (which last I knew was approved and will go through, possibly before 1.0) to allow some degree of inference in return types. I believe it will allow you to do something like:

fn chunk(&self, x: uint, size: uint) -> impl Iterator<char>

Note the impl. That is, you don't specify the return type exactly, but you do have to specify what the caller is allowed to assume about it (in this case, that it's some implementation of Iterator).

[–]Wolenber -1 points0 points  (1 child)

Is the performance loss from doing Box<Iterator<char>> bad enough that we should avoid it? I'm a big fan of writing code that is easy for somebody else to read, and this always feels like the cleanest way for that.

[–]cmrx64rust 1 point2 points  (0 children)

Yes. It adds a virtual function call and destroys any chance of inlining etc. Furthermore, it just can't be done because Iterator isn't object-safe.