Rustコトハジメ

プログラミング言語Rustと競プロに関する情報をお届けします。

Deref is not called before IntoIterator

In the previous post

www.rustforbeginners.com

we left a question. And this post is the answer. I will share the interesting finding from a simple experiment.

The question

The Rust library has implementations that convert &Vec<T> into Iter<t> and &mut Vec<T> into IterMut<T> in Vec<T> but I thought the implementation can be removed and it's just a optimization to bypass the indirect conversions for frequent path because these two steps

  1. &Vec<T> is converted into &[T] by Deref.
  2. IntoIterator from &[T] to Iter<T> is found and applied.

should be executed.

Our task is to make an answer to this question by a simple experiment.

Experiment

use std::ops::Deref;

struct MyVec<T> {
    inner: Vec<T>,
}

impl<T> Deref for MyVec<T> {
    type Target = Vec<T>;
    fn deref(&self) -> &Vec<T> {
        &self.inner
    }
}

fn main() {
    let v = MyVec {
        inner: vec![1,2,3,4]
    };
    for e in &v {
        println!("{}", e);
    }
}

The aim here is that

  1. &MyVec should be deref-ed into &Vec.
  2. The IntoIterator implementation of&Vec should be found and applied.

Result

The result is however, it fails to compile. The error message tells us that the deref is ignored.

error[E0277]: `&MyVec<{integer}>` is not an iterator
  --> prog.rs:18:14
   |
18 |     for e in &v {
   |              ^^ `&MyVec<{integer}>` is not an iterator
   |
   = help: the trait `std::iter::Iterator` is not implemented for `&MyVec<{integer}>`
   = note: required by `std::iter::IntoIterator::into_iter`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.

Discussion

We may be able to understand this behavior as follows.

If we write a for-loop as function the type signature would be like

fn forloop<I, F>(it: I, f: F) where
    I: IntoIterator,
    F: FnMut(I::Item)

In the meantime, in Programming Rust from O'Reilly it says

The deref coercions come with a caveat that can cause some confusion: Rust applies them to resolve type conflicts, but not to satisfy bounds on type variables.

and we can reason about the behavior that Rust compiler doesn't apply Deref to resolve the boundary I: IntoIterator.