rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
99.14k stars 12.8k forks source link

Box<Iterator> (and &mut Iterator) does not implement Iterator #20953

Closed BurntSushi closed 9 years ago

BurntSushi commented 9 years ago

This code fails to compile:

fn main() {
    let mut shrinker = Box::new(vec![1].into_iter()) as Box<Iterator<Item=i32>>;
    println!("{:?}", shrinker.next());
    for v in shrinker { println!("{:?}", v); }
}

error message:

[andrew@Liger play] rustc scratch2.rs 
scratch2.rs:32:14: 32:22 error: `for` loop expression has type `Box<core::iter::Iterator>` which does not implement the `Iterator` trait; maybe try .iter()
scratch2.rs:32     for v in shrinker { println!("{:?}", v); }
                            ^~~~~~~~
error: aborting due to previous error

A similar error occurs if &mut Iterator is used instead of Box<Iterator>. However, if you comment out the for loop, the call to next works just fine. I thought maybe it had something to do with auto-deref, so I tried dereferencing the iterator:

for v in *shrinker { println!("{:?}", v); }

but rustc dumps core:

[andrew@Liger play] rustc scratch2.rs 
rustc: /home/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/llvm/lib/IR/Instructions.cpp:1086: void llvm::StoreInst::AssertOK(): Assertion `getOperand(0)->getType() == cast<PointerType>(getOperand(1)->getType())->getElementType() && "Ptr must be a pointer to Val type!"' failed.
Aborted (core dumped)

I'm not sure, but this may be related to #20605? rustc dumps core when I try &mut Iterator too.

rustc version:

[andrew@Liger play] rustc --version
rustc 1.0.0-nightly (44a287e6e 2015-01-08 17:03:40 -0800)
Gankra commented 9 years ago

CC @nikomatsakis

chris-morgan commented 9 years ago

I think the problem is worse than this; trait objects in general don’t seem to be being recognised as implementing their traits:

trait Trait { }

fn a(x: &Trait) { try(x); }
fn b(x: &mut Trait) { try(x); }
fn c(x: Box<Trait>) { try(x); }
fn try<T: Trait>(t: T) { }

fn main() { }

All three fail:

i.rs:3:19: 3:22 error: the trait `Trait` is not implemented for the type `&Trait`
i.rs:3 fn a(x: &Trait) { try(x); }
                         ^~~
i.rs:4:23: 4:26 error: the trait `Trait` is not implemented for the type `&mut Trait`
i.rs:4 fn b(x: &mut Trait) { try(x); }
                             ^~~
i.rs:5:23: 5:26 error: the trait `Trait` is not implemented for the type `Box<Trait>`
i.rs:5 fn c(x: Box<Trait>) { try(x); }
                             ^~~
error: aborting due to 3 previous errors
eddyb commented 9 years ago

I'm not sure this is a bug at all. You're pretty much asking for an automatic impl<T> Trait for &mut T where T: Trait - I did come up with a Deref & DerefMut-based model that would kinda work for this and IIRC @nikomatsakis pointed out that object-safe traits would eventually qualify for this. We do have object safety now, so it might be doable, but I wouldn't bet on it making into 1.0. In the meanwhile, does .by_ref() work? If it doesn't, is it just the case of missing ?Sized in a couple places?

japaric commented 9 years ago

This a particular case of #20617. See comments over there. @nikomatsakis is leaning towards an opt-in derive syntax extension that handles the boiler plate code, instead of automatic impls by the compiler.

bluss commented 9 years ago

ByRef for trait objects seems to hit the same llvm assertion:

fn main() {
    struct ByRef<'r, I: ?Sized>(&'r mut I) where I: 'r;
    impl<'r, I: ?Sized> Iterator for ByRef<'r, I> where
        I: 'r + Iterator
    {
        type Item = <I as Iterator>::Item;
        fn next(&mut self) -> Option<<I as Iterator>::Item>
        {
            self.0.next()
        }
    }

    let mut it = Box::new(0..10) as Box<Iterator<Item=i32>>;
    assert_eq!(it.next(), Some(0));

    let mut jt: &mut Iterator<Item=i32> = &mut *it;
    assert_eq!(jt.next(), Some(1));

    let mut r = ByRef(jt);
    assert_eq!(r.next(), Some(2));    // No ICE without this line.
}

output:

rustc: /build/rust-git/src/rust/src/llvm/lib/IR/Instructions.cpp:1086: void llvm::StoreInst::AssertOK(): Assertion `getOperand(0)->getType() == cast<PointerType>(getOperand(1)->getType())->getElementType() && "Ptr must be a pointer to Val type!"' failed.
BurntSushi commented 9 years ago

I'm not sure this is a bug at all. You're pretty much asking for an automatic impl<T> Trait for &mut T where T: Trait

Interesting. So if I have a value with type Box<T> where T is a trait, then we can't say that Box<T> implements T in the general case? (Assuming object safety.) So my guess is that Box<Iterator> can still call next because of auto deref? Shouldn't that mean it can work with for? Maybe that is what #20605 is about... You have to explicitly deref a Box<Iterator> to use it in for?

(Sorry for all the questions. I'm just confused!)

eddyb commented 9 years ago

@BurntSushi doing autoderef on the iterator in for seems like something reasonable to have.

20605 looks like the compiler is mistakenly treating an unsized lvalue as an rvalue, in trans.

nikomatsakis commented 9 years ago

On Mon, Jan 12, 2015 at 01:33:08PM -0800, Andrew Gallant wrote:

Interesting. So if I have a value with type Box<T> where T is a trait, then we can't say that Box<T> implements T in the general case?

Actually, I believe it works with Box, but not with the other types.

hicqu commented 7 years ago

Just thank you for this issue. It resolves all my problems perfectly!