How to implement iterator using trait - rust

I have a struct called Library, which has a vector of strings (titles). I have implemented an iterator for this. Here is my code.
#[derive(Debug, Clone)]
struct Library {
books: Vec<String>
}
impl Iterator for Library {
fn next(&mut self) -> Option<Self::Item> {
...
}
}
Now, I am trying to implement an Iterator using a trait, like this:
fn foo(x: Vec<u32>) -> impl Iterator<Item=u32> {
//Unsure if correct method
fn next() -> Option<...> {
x.into_iter()....
}
}
But I'm unsure of how to proceed in this case. Do I simply have to define a next() method again? That doesn't seem to be the case, according to other resources. Why is that? Shouldn't an iterator (which is being returned) have a next() method?
What is the general method for implementing an iterator this way?

You wouldn't implement the trait directly on Library. The library is not an iterator, but it is something that could be iterated by an iterator.
Instead, just declare a method that returns an iterator, and you can simply return an iterator directly from your vector. There is no need for a custom iterator implementation. For example:
impl Library {
fn iter(&self) -> impl Iterator<Item=&String> {
self.books.iter()
}
}
For your second case, you can turn the vector into an iterator using into_iter(), provided by the IntoIterator trait:
fn foo(x: Vec<u32>) -> impl Iterator<Item=u32> {
x.into_iter()
}

Related

Composition over inheritance, sure, but do we have any syntactic sugar for the function passthrough boilerplate?

If I have a struct implementing a trait, and then I encapsulate that struct in another struct, is there an easy way to pass through all the function calls to implement that trait for the second struct?
trait HasAnX {
fn get_x(&self) -> i32;
}
struct StructA {
x: i32
}
impl HasAnX for StructA {
fn get_x(&self) -> i32 {
self.x
}
}
struct StructB {
a: StructA
}
// This is the part I don't want to have to write out for every function in StructA
impl HasAnX for StructB {
fn get_x(&self) -> i32 {
self.a.get_x()
}
}
I think half of the problem is that I'm not even sure what this problem is called.
The need to implement HasAnX for StructB usually comes from looking at the problem with inheritance in mind.
To avoid this situation try to work with only the part you really care for.
If you just want to call a method on a you can do so from the outside:
struct_b.a.get_x();
If you want to work with anything like a StructA you can just implement AsRef<StructA> for it, similarly you can use AsMut for mutable access.
Then functions can just take an impl AsRef<StructA> and don't need to care what actual type it is, with the added benefit that it now also can take owned types and references.
impl AsRef<StructA> for StructB {
fn as_ref(&self) -> &StructA {
&self.a
}
}
// you can pass in any of `StructA`, `&StructA`, `StructB`, `&StructB`
pub fn reads_from_a(a: impl AsRef<StructA>) {
let a = a.as_ref();
println!("{}", a.get_x());
}
If your StructB is a sort of smart pointer you can implement Deref and just use StructAs methods directly on StructB
use std::ops::Deref;
impl Deref for StructB {
type Target = StructA;
fn deref(&self) -> &StructA {
&self.a
}
}
//...
struct_b.get_x();

Composition and reusability in Rust compiled code

I am new to the composition approach with Rust, and I am having a hard time trying to figure out whether I could make my code more efficient / smaller.
Let us assume that I have a base struct BaseStruct:
struct BaseStruct {
values: Vec<i32>,
}
impl BaseStruct {
fn new() -> Self {
Self{values: vec![]}
}
}
Then, I define a AsBase trait to ease the composition process:
/ Trait used to do composition
trait AsBase {
fn as_base(&mut self) -> &mut BaseStruct;
// Note that add_val has a default implementation!
fn add_val(&mut self, val: i32) {
// let us assume that this method has hundreds of lines of code
self.as_base().values.push(val);
}
}
// Note that BaseStruct implements the AsBase trait!
impl AsBase for BaseStruct {
fn as_base(&mut self) -> &mut BaseStruct {
self
}
}
Note that BaseStruct implements the AsBase trait! Otherwise, we couldn't add a value to the vector.
Then, I derive the behavior of the base struct using composition:
// Derived struct and handy functions
struct DerivedStruct {
base: BaseStruct,
}
impl DerivedStruct {
fn new() -> Self {
Self{base: BaseStruct::new()}
}
}
// Derived struct also implements the AsBase trait
impl AsBase for DerivedStruct {
fn as_base(&mut self) -> &mut BaseStruct {
&mut self.base
}
}
So now, I can add values to the inner vector of my derived struct using the trait method!
fn main() {
let mut base = BaseStruct::new();
base.add_val(1);
let mut derived = DerivedStruct::new();
derived.add_val(1); // With composition and AsBase trait, I achieve "inheritance"
}
Here you have a playground with this example.
However, what if the add_val default method was very complex and required hundreds of lines of code? Would Rust generate a different method add_val for every struct implementing the AsBase trait? Or is the compiler smart enough to detect that they can share the same function?
Let me try to be clearer: would this alternative implementation be smaller is size, as it explicitly uses the same method?
// Base struct and handy associated methods
struct BaseStruct {
values: Vec<i32>,
}
impl BaseStruct {
fn new() -> Self {
Self{values: vec![]}
}
fn add_val(&mut self, val: i32) {
// Let us assume that this method is hundreds of lines long
self.values.push(val);
}
}
// Trait used to do composition
trait AsBase {
fn as_base(&mut self) -> &mut BaseStruct;
// Note that add_val has a default implementation!
fn add_val(&mut self, val: i32) {
self.as_base().add_val(val);
}
}
// Note that BaseStruct does NOT implement the AsBase trait to avoid naming collision!
// Derived struct and handy functions
struct DerivedStruct {
base: BaseStruct,
}
impl DerivedStruct {
fn new() -> Self {
Self{base: BaseStruct::new()}
}
}
// Derived struct also implements the AsBase trait
impl AsBase for DerivedStruct {
fn as_base(&mut self) -> &mut BaseStruct {
&mut self.base
}
}
fn main() {
let mut base = BaseStruct::new();
base.add_val(1);
let mut derived = DerivedStruct::new();
derived.add_val(1); // With composition and AsBase trait, I achieve "inheritance"
}
(Also, note that I couldn't implement the AsBase trait for BaseStruct due to naming collisions, I don't know if there is a workaround to avoid this other than changing the names of the methods).
Here you have a playground for the alternative version.
Yes, the compiler will generate a new instance of add_val() for each type. It may collapse them if they use the same machine code, but it will not if they don't. If you want to avoid that, a common way is to define a nested function (see Why does std::fs::write(...) use an inner function?):
fn add_val(&mut self, val: i32) {
fn inner(this: &mut BaseStruct) {
// let us assume that this method has hundreds of lines of code
base.values.push(val);
}
inner(self.as_base());
}
However, what you're doing is not composition. Rather, it's emulating inheritance with composition. When using the composition principle, you should not (usually) have an AsBase trait because you should not treat DerivedStruct as BaseStruct. This is not "is-a" relationship, this is a "has-a" relationship. If a method needs a BaseStruct, pass it a reference to the field directly and let it perform its work.

into_iter() available on Iterator and multiple times

I am new to rust and was going over iterators and this where I got blocked.So if I am implementing Iterator directly and not IntoIterator,how come I am able to call into_iter
(and too multiple times).My understanding is if I implemented IntoIterator,I should be going to Iterator via into_iter.But if I am able to implement Iterator,why do I still need into_iter(would be called implicitly if i didnt give I guess for for in )
struct StrSplit<'a> {
rest: &'a str,
delim: &'a str,
}
impl<'a> StrSplit<'a> {
fn new(haystack: &'a str, delim: &'a str) -> Self {
Self {
rest: haystack,
delim,
}
}
}
impl<'a> Iterator for StrSplit<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
let to_return;
if let Some(next) = self.rest.find(self.delim) {
to_return = &self.rest[..next];
self.rest = &self.rest[next + self.delim.len() + 1..];
return Some(to_return);
} else if !self.rest.is_empty() {
to_return = self.rest;
self.rest="";
return Some(to_return);
}
return None;
}
}
fn main() {
let check = StrSplit::new("this was to check what was to be checked", "was");
let iterr = check.into_iter().into_iter().into_iter();
for part in iterr {
println!("{}", part);
}
}
If you're new to Rust this might be a bit subtle, but it's a thing that you'll see quite often with the traits of the standard library:
You correctly note that into_iter is a method of the IntoIterator trait. You also note that you implemented Iterator for your struct, but not IntoIterator. So what gives?
Checking the methods defined on Iterator, there's no into_iter to be found.
But now if you check the documentation for IntoIterator, and scroll down to the list of Implementors https://doc.rust-lang.org/std/iter/trait.IntoIterator.html#implementors
you will find the following snippet:
impl<I> IntoIterator for I
where
I: Iterator
This is called a blanket implementation: For any type T that implements the Iterator trait, there will be automatically an implementation for the IntoIterator trait!
Because your struct implements Iterator, it also gets IntoIterator implemented.
There's some other examples of this in the standard library. For example, if you implement From<A> for some type B, you automatically get the Into<B> trait implemented for type A.
Another example is ToString:
impl<T> ToString for T where
T: Display + ?Sized,
Any type that implements the Display trait gets the ToString trait for free!

A parameter that accepts a sequence of trait objects and iterate through it multiple times in Rust

I have a function that takes a sequence of trait objects and iterates through it multiple times, e.g.,
trait Animal {
fn make_noise(&self);
}
fn make_lots_of_noises(animals: &[&dyn Animal]) {
animals.iter().for_each(|animal| animal.make_noise());
animals.iter().for_each(|animal| animal.make_noise());
animals.iter().for_each(|animal| animal.make_noise());
}
But I want the function to be able to operate on both borrowed and owned data structures. Here are the options I tried:
&[&dyn Animal] as shown in the code fragment. Problem: if I have owned trait objects, e.g., animals: &[Box<dyn Animal>], then I have to call make_lots_of_noises(animals.map(Box::as_ref).collect::<Vec<_>>().as_slice(), which involves unnecessary allocation on the heap.
&[&R] where R: AsRef<dyn Animal> + ?Sized. Problem: T doesn't implement AsRef<T>! It now works with all kinds of smart pointers, but not &[&dyn Animal].
Use the Borrow trait, which does have the impl Borrow<T> for T you want.
use std::borrow::Borrow;
trait Animal {
fn make_noise(&self);
}
struct Mouse;
impl Animal for Mouse {
fn make_noise(&self) { /* squeak */ }
}
fn make_lots_of_noises<A: Borrow<dyn Animal>>(animals: &[A]) {
animals.iter().for_each(|animal| animal.borrow().make_noise());
animals.iter().for_each(|animal| animal.borrow().make_noise());
animals.iter().for_each(|animal| animal.borrow().make_noise());
}
fn main() {
make_lots_of_noises(&[&Mouse as &dyn Animal]);
make_lots_of_noises(&[Box::new(Mouse) as Box<dyn Animal>]);
}

How do I write a trait method that knows the implementor is [u8]?

I'm implementing a trait for &[u8] but I cannot use self in the trait implementation. I assume that the trait can not detect the type and I should use a where clause, but I don't know how can I use it without an implementor.
use std::fmt::Debug;
pub trait Xor: Debug {
fn xor(&self, key_bytes: &[u8]) -> &[u8] {
for n in &self[..] {
dbg!(n);
}
unimplemented!()
}
}
impl Xor for [u8] {}
fn main() {
let xa = b"1234";
xa.xor(b"123");
}
Playground
error[E0608]: cannot index into a value of type `&Self`
--> src/main.rs:5:19
|
5 | for n in &self[..] {
| ^^^^^^^^
There are two places you can write the body of a trait method:
inside the trait itself, as a provided method
inside an impl block.
If a method is not provided, it is required, which means all implementors have to write their own body in the appropriate impl block.
Provided methods can only use properties that are common to all the implementors of the trait, which means you can only use other trait methods or methods of supertraits (like : Debug). But methods in an impl block may use properties that are specific to the type implementing the trait. You want to use something specific to [u8] -- indexing via [..] -- so xor should be a required method:
pub trait Xor {
fn xor(&self, key_bytes: &[u8]) -> &[u8];
}
impl Xor for [u8] {
fn xor(&self, key_bytes: &[u8]) -> &[u8] {
for n in &self[..] {
dbg!(n);
}
unimplemented!()
}
}
Provided methods are usually for conveniences that only use other methods of the same trait, like most of the methods on Iterator (see Why don't we implement all the functions from Iterator to implement an iterator?).
is it possible to implement the trait for several kind of types [without writing multiple impl blocks]?
Yes, if there is a trait that exposes the functionality you would use to write Xor, you may use that trait to write a generic impl. For example, String and [u8] both implement AsRef<[u8]>, so you can use that to write an impl that applies to both:
impl<T: ?Sized + AsRef<[u8]>> Xor for T {
fn xor(&self, key_bytes: &[u8]) -> &[u8] {
for n in &self.as_ref()[..] {
dbg!(n);
}
unimplemented!()
}
}
Playground link.
See also
How does Rust know which trait methods are required or provided? for more on provided and required trait functions
What does the question mark mean in a type parameter bound? for what ?Sized means

Resources