Obligatory: I apologize if this has already been discussed.
Motivation
Currently, when writing a function that returns:
iter::range(1u, 10u).map(|x| x*x).filter(|x| x % 2u == 0)
One has to write one of:
// option 1
fn test<'s>() -> iter::Filter<'s, uint, iter::Map<'s, uint, uint, iter::Range<uint>>> {
iter::range(1u, 10u).map(|x| x*x).filter(|x| x % 2u == 0)
}
// opiton 2
fn test<'s>() -> Box<Iterator<uint> + 's> {
box iter::range(1u, 10u).map(|x| x*x).filter(|x| x % 2u == 0)
}
Option 1 is painful to write and option 2 requires a heap allocation.
Primary Proposal
My primary proposal is to allow one to write the following:
fn test<'s>(square: bool) -> Iterator<uint>+'s {
iter::range(1u, 10u).map(|x| x*x).filter(|x| x % 2u == 0)
}
This would be equivalent to option 1 except that the caller would only be allowed to access methods accessible through the Iterator trait (this would also help with the problem of exposing private types in public methods).
Additional Proposal
Unfortunately, this only handles the case of a single return type. However, this isn’t always the case:
fn test<'s>(square: bool) -> ??? {
if square {
iter::range(1u, 10u).map(|x| x*x).filter(|x| x % 2u == 0)
} else {
iter::range(1u, 10u).filter(|x| x % 2u == 0)
}
}
I propose, again, that the following syntax be allowed:
fn test<'s>(square: bool) -> Iterator<uint>+'s {
if square {
iter::range(1u, 10u).map(|x| x*x).filter(|x| x % 2u == 0)
} else {
iter::range(1u, 10u).filter(|x| x % 2u == 0)
}
}
which would be functionally equivalent to:
fn test<'s>(square: bool) -> _test_ReturnType<'s> {
if square {
A(iter::range(1u, 10u).map(|x| x*x).filter(|x| x % 2u == 0))
} else {
B(iter::range(1u, 10u).filter(|x| x % 2u == 0))
}
}
enum _test_ReturnType<'s> {
// One for each possible return type
A(iter::Filter<'s, uint, iter::Map<'s, uint, uint, iter::Range<uint>>>),
B(iter::Filter<'s, uint, iter::Range<uint>>)
}
impl<'s> Iterator<uint> for _test_ReturnType<'s> {
fn next(&mut self) -> Option<uint> {
match self {
&A(ref mut inner) => inner.next(),
&B(ref mut inner) => inner.next()
}
}
}
Note: This is just an illustration, not a implementation recommendation. For the actual implementation, I would resolve funtions on the returned “union” the same way rust currently does on a boxed traits.
Drawbacks
The primary drawback I can see is that methods that return traits by value would not be callable on trait objects because, like methods that return Self, the underlying return type of these methods is implementation dependent.