Introducing variance to objects

Make test_covariance compile by making BoolStream<'a> covariant over 'a. Restrictions:

  • Can only change implementation details of BoolStream and its methods and add extra items outside of what's given, i.e. no signature/test change.
  • Changed version must behave the same way as the original.

Consider the following code:

#![allow(unused)]
fn main() {
pub struct BoolStream<'a>(
    Box<dyn 'a + FnOnce() -> (bool, BoolStream<'a>)>,
);

impl<'a> BoolStream<'a> {
    pub fn new(f: impl 'a + FnOnce() -> (bool, Self)) -> Self {
        Self(Box::new(f))
    }

    pub fn next(self) -> (bool, Self) {
        self.0()
    }
}

fn test_covariance<'a: 'b, 'b>(e: BoolStream<'a>) -> BoolStream<'b> {
    e
}
}

Why does it fail to compile?

#![allow(unused)]
fn main() {
pub struct BoolStream<'a>(
    // change this
/*
    Box<dyn 'a + FnOnce() -> (bool, BoolStream<'a>)>,
*/ Box<dyn 'a + Next>
);

impl<'a> BoolStream<'a> {
    pub fn new(f: impl 'a + FnOnce() -> (bool, Self)) -> Self {
        // maybe change this
/*
        Self(Box::new(f))
*/ Self(Box::new(Wrapper(f, PhantomData)))
    }

    pub fn next(self) -> (bool, Self) {
        // maybe change this
/*
        self.0()
*/ self.0.next()
    }
}

fn test_covariance<'a: 'b, 'b>(e: BoolStream<'a>) -> BoolStream<'b> {
    e
}

use std::marker::PhantomData;
struct Wrapper<'a, F>(F, PhantomData<&'a ()>);
trait Next {
fn next<'t>(self: Box<Self>) -> (bool, BoolStream<'t>) where Self: 't;
}
impl<'a, F: 'a + FnOnce() -> (bool, BoolStream<'a>)> Next for Wrapper<'a, F> {
fn next<'t>(self: Box<Self>) -> (bool, BoolStream<'t>) where Self: 't { self.0() }
}

/// Tests to check that behaviour stays the same.

fn get_true<'a>() -> BoolStream<'a> {
    BoolStream::new(|| (true, get_false()))
}

fn get_false<'a>() -> BoolStream<'a> {
    BoolStream::new(|| (false, get_true()))
}

let e = get_true();
let (x, e) = e.next();
assert!(x);
let (x, e) = e.next();
assert!(!x);
let (x, e) = e.next();
assert!(x);
let (x, e) = e.next();
assert!(!x);
test_covariance(e);
}