As always, happy to answer questions, provide context, etc.
As always, happy to answer questions, provide context, etc.
fn foo(x: i32) -> Box<Iterator<Item = i32>> {
let iter = vec![1, 2, 3]
.into_iter()
.map(|x| x + 1);
if x % 2 == 0 {
Box::new(iter.filter(|x| x % 2 == 0))
} else {
Box::new(iter)
}
}
Why is it that I can't return `impl Iterator<Item = i32>`? Doesn't the `Filter` type implement `Iterator` for the same associated type?By contrast, if I returned a boxed trait, then calling the trait method requires a dynamic lookup to find the method on the boxed object, and then jumping to that function (dynamic dispatch). In this example, `iter.map(..)` and `iter.filter(..)` return two different implementations of the Iterator trait, so dynamic dispatch is required.
In general, if your function returns multiple possible implementations of a given trait, then the compiler cannot know where the trait methods will be statically, so it is impossible to do static dispatch. Since impl Trait wants to guarantee static dispatch, it requires that only one possible implementation of the trait be returned.
The offset of the function pointers is always statically known for a given trait/interface type, it's just the actual vtable instance that may not be known, ie. the concrete type implementing that trait/interface. A statically known vtable instance that can be inlined/monomorphized is static dispatch, and if it's not known at compile-time it must be dynamically dispatched.
"calling the trait method requires a dynamic lookup to find the method on the boxed object"
It sounds like all you want to say is
"calling the trait method requires a dynamic lookup to find [the vtable instance, which is then used to find] the method on the boxed object"
> then the function pointer will always be in the same place on the object in memory (static dispatch)
As I said, static vs. dynamic dispatch is about knowledge of the vtable instance, not about the offset into the object or the vtable. All of the offsets are always known statically, it's merely what you're indexing into that may or may not be known statically.
Maybe I'm being pedantic, but I've found that there's a lot of misunderstanding surrounding static vs. dynamic dispatch.
There’s a crate on crates.io for it if you want to use it for something.
There is some discussion about it possibly being sugar for an anonymous enum in the future, but that’s not what it is right now.
In Rust (most languages), by default the compiler needs to set aside space on the stack for each return value. So it needs a constantly known size (and shape) to create the slot on the stack. You need to flip to dynamic dispatch, ie a pointer to an object (Java's default), that will be placed on the stack as a reference to the unknown size/shape of the thing at the end of the pointer when the size/shape is unknown. A pointer always has a constant size on the stack.
In this example, `impl Trait` is just saying I want the compiler to figure out the size/shape of the thing being returned for me, and allocate that to the stack at the call site of the function. What this means is that even with `impl Trait` you must return a thing that has the same size/shape.
Steve's answer mentions a common pattern used to create constant size/shape by using an enum for the wrapper type to return two different types on the stack from the function. The only other option is to put something with unknown size behind a pointer, ie Box<Trait> or &Trait, and thus pay the expense of dynamic dispatch.
Is there anywhere I can get a quick recap of major features Rust is planning to eventually maybe implement, and their state? I mean major features like NLL, impl trait, etc.
IIRC: NLL are nightly already, const generics in planning stage, and what happened to CTFE? For us not in the know, a sneak peek on what's coming/being discussed is very exciting, but browsing the issue/RFC tracker is sometimes a PITA and it's hard to keep up to date, especially after getting used to the workarounds.
I mean, just yesterday I had to do some weird stuff to a for loop in order to iterate over all u8s in a cleanish way.
Lovely, lovely work. I'm especially excited to see how clever the ref/ref-mut `match` inferring is. If it's reliable, that's gonna remove quite a bit of friction for newcomers. So cool!
Being able to return anonymous trait impls from inside functions could eliminate a lot of what seems like boilerplate structs/impls from Rust code. As an example, the code in futures-util that adds combinators to a Future defines a type (Then, Fuse, Map, etc) for each combinator function. With those functions now able to use 'impl Future<...>' as the return type, being able to actually type 'return impl Future<...>' could make code like that a lot less cluttered.
While I'm sure it won't mess anyone up, the range for the i128 is hard to interpret. It looks like -xxxxx - xxxxxx but with the extreme number of digits it looks like two negative numbers or a subtraction problem.
[edit: u128 -> i128]
Is the non-lexical lifetime improvements work locked to a particular release yet?
Also, now that the async/await RFC has been merged, is its implementation in nightly or stable going to be squeezed in for the 2018 roadmap plans?
As for non-lexical lifetimes, there's lots that's been implemented but much of it is still experimental; follow Niko Matsakis' blog series at http://smallcultfollowing.com/babysteps/blog/2018/04/27/an-a... to stay up to date with what's happening.
Question: Is there any way to preview what the book's typesetting looks like, since there's praise in the release note?
It shows off Chapter 3, and yes, it really looks great.