Skip to content

Commit

Permalink
Merge pull request #5 from orxfun/object-safe-iterables
Browse files Browse the repository at this point in the history
obj-safe module and object safe collections and iterables are defined
  • Loading branch information
orxfun authored Dec 9, 2024
2 parents f41e037 + 74d8b60 commit 630e904
Show file tree
Hide file tree
Showing 78 changed files with 2,560 additions and 218 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "orx-iterable"
version = "1.0.4"
version = "1.1.0"
edition = "2021"
authors = ["orxfun <orx.ugur.arikan@gmail.com>"]
description = "Defines and implements Iterable, Collection and CollectionMut traits to represent types that can be iterated over multiple times."
Expand All @@ -15,3 +15,7 @@ orx-self-or = "1.0"
[dev-dependencies]
arrayvec = { version = "0.7.6", default-features = false }
smallvec = { version = "1.13.2", default-features = false }

[features]
default = ["std"]
std = []
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Not all iterables are collections storing elements. For instance, a *Range* is i

> More general immutable iterable trait `Iterable` is defined and implemented for collections and cloneable iterators, and it is open for custom iterables.
In addition, **object safe variants** of these traits, `IterableObj`, `CollectionObj` and `CollectionMutObj` are provided, please see section E for details.

## A. Collection and CollectionMut

The core method of the Collection trait is `iter(&self)` which returns an iterator yielding shared references; i.e., `&Item`.
Expand Down Expand Up @@ -491,6 +493,43 @@ assert_eq!(numbers.iter().collect::<Vec<_>>(), [&5, &8, &4, &3]);
</details>
<br />

## E. Object Safe Iterables and Collections

You may refer to the rust book for an introduction of [trait objects](https://doc.rust-lang.org/book/ch17-02-trait-objects.html).

The requirement can be demonstrated with the following example. Assume that we have a type, say `Repo`, which needs to hold a String collection, say `names`. We want to allow for different collections. We can achieve this by making our type generic over the collection type; i.e., `C: Collection`. Then, our type becomes `Repo<C>`. This has the advantage of monomorphization which would give us zero cost abstraction.

```rust
use orx_iterable::*;

struct Repo<C: Collection<Item = String>> {
names: C,
}
```

However, this is a more complex type than only `Repo` (the complexity can get out of hand if we have, for instance, four collections in our repo). We can achieve the simpler type by making our collection a trait object, adding an indirection such as `Box` or `Rc` and using it as our field. In this case, our code will use dynamic dispatch which is slower. Sometimes the difference is negligible in our application. If we prefer simplicity in this tradeoff, we can use the trait object approach.

However, we cannot use the `Iterable`, `Collection` or `CollectionMut` traits, because trait objects have certain restrictions. For this purpose, object safe variants `IterableObj`, `CollectionObj` and `CollectionMutObj` are introduced. These object safe variants have `boxed_iter` and `boxed_iter_mut` methods returning iterators in a box, rather than *iter* and *iter_mut*.

The conditions to implement these variants are identical to the original traits. Therefore, if a type implements `Collection` it also implements `CollectionObj`, and vice versa.

Now we can achieve our simpler `Repo` type.

```rust
use orx_iterable::obj_safe::*;

struct Repo {
names: Box<dyn CollectionObj<Item = String>>,
}
```

In order to use object safe iterables and collections please add `--features std` if default features are not used, and use `use orx_iterable::{*, obj_safe::*}` to import dependencies rather than `use orx_iterable::*`.

For a comparison of both generic and trait object approaches, please see the examples:

* [tests/fields_of_generic_iterables.rs](https://github.com/orxfun/orx-iterable/blob/main/tests/fields_of_generic_iterables.rs)
* [tests/fields_of_iterable_objects.rs](https://github.com/orxfun/orx-iterable/blob/main/tests/fields_of_iterable_objects.rs)

## Contributing

Contributions are welcome! If you notice an error, have a question or think something could be improved, please open an [issue](https://github.com/orxfun/orx-iterable/issues/new) or create a PR.
Expand Down
134 changes: 0 additions & 134 deletions examples/stats.rs

This file was deleted.

3 changes: 2 additions & 1 deletion src/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,8 @@ where
{
type Item = <X as IntoIterator>::Item;

type Iterable<'i> = &'i X
type Iterable<'i>
= &'i X
where
Self: 'i;

Expand Down
3 changes: 2 additions & 1 deletion src/collection_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ where
for<'a> &'a X: IntoIterator<Item = &'a <X as IntoIterator>::Item>,
for<'a> &'a mut X: IntoIterator<Item = &'a mut <X as IntoIterator>::Item>,
{
type IterMut<'i> = <&'i mut X as IntoIterator>::IntoIter
type IterMut<'i>
= <&'i mut X as IntoIterator>::IntoIter
where
Self: 'i;

Expand Down
9 changes: 8 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
)]
#![no_std]

#[cfg(any(test, feature = "std"))]
extern crate std;

mod collection;
mod collection_mut;
mod iterable;
Expand All @@ -20,9 +23,13 @@ mod producing_iterables;
pub mod sources;
/// Module defining transformations among iterables.
pub mod transformations;

/// Object safe variants of Iterable, Collection and CollectionMut traits.
#[cfg(feature = "std")]
pub mod obj_safe;

pub use collection::Collection;
pub use collection_mut::CollectionMut;
pub use iterable::Iterable;

pub use sources::{empty, empty_col, once, once_col, repeat, repeat_n};
pub use transformations::IntoCloningIterable;
75 changes: 75 additions & 0 deletions src/obj_safe/collection_mut_obj.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::obj_safe::collection_obj::CollectionObj;
use std::boxed::Box;

/// In addition to [`boxed_iter`], a `CollectionMutObj` provides the [`boxed_iter_mut`] method which returns a boxed
/// iterator over mutable references of elements of the collection.
///
/// It is the object safe counterpart of [`CollectionMut`] trait which can conveniently be made into a trait object.
///
/// Note that for collections, `CollectionMutObj` is implicitly implemented and readily available.
/// Please refer to [`CollectionMut`] documentation for details of automatic implementations.
///
/// In order to use object safe iterables and collections please add `--features std` and use
/// `use orx_iterable::{*, obj_safe::*}` to import dependencies rather than `use orx_iterable::*`.
///
/// [`Iterable`]: crate::Iterable
/// [`Item`]: crate::obj_safe::CollectionMutObj::Item
/// [`boxed_iter`]: crate::obj_safe::CollectionObj::boxed_iter
/// [`boxed_iter_mut`]: crate::obj_safe::CollectionMutObj::boxed_iter_mut
/// [`CollectionMut`]: crate::CollectionMut
///
/// # Examples
///
/// ```
/// use orx_iterable::obj_safe::*;
/// use arrayvec::ArrayVec;
/// use smallvec::{smallvec, SmallVec};
/// use std::collections::{LinkedList, VecDeque};
///
/// /// first computes sum, and then adds it to each of the elements
/// fn increment_by_sum(numbers: &mut dyn CollectionMutObj<Item = i32>) {
/// let sum: i32 = numbers.boxed_iter().sum();
///
/// for x in numbers.boxed_iter_mut() {
/// *x += sum;
/// }
/// }
///
/// // example collections that automatically implement CollectionMut
///
/// let mut x = [1, 2, 3];
/// increment_by_sum(&mut x);
/// assert_eq!(x, [7, 8, 9]);
///
/// let mut x = vec![1, 2, 3];
/// increment_by_sum(&mut x);
///
/// let mut x = LinkedList::from_iter([1, 2, 3]);
/// increment_by_sum(&mut x);
///
/// let mut x = VecDeque::from_iter([1, 2, 3]);
/// increment_by_sum(&mut x);
///
/// let mut x: SmallVec<[_; 128]> = smallvec![3, 5, 7];
/// increment_by_sum(&mut x);
///
/// let mut x = ArrayVec::<_, 16>::new();
/// x.extend([3, 5, 7]);
/// increment_by_sum(&mut x);
/// ```
pub trait CollectionMutObj: CollectionObj {
/// Creates a new iterator in a box yielding mutable references to the elements of the collection; i.e.,
/// type of elements is `&mut Item`.
fn boxed_iter_mut(&mut self) -> Box<dyn Iterator<Item = &mut Self::Item> + '_>;
}

impl<X> CollectionMutObj for X
where
X: IntoIterator,
for<'a> &'a X: IntoIterator<Item = &'a <X as IntoIterator>::Item>,
for<'a> &'a mut X: IntoIterator<Item = &'a mut <X as IntoIterator>::Item>,
{
fn boxed_iter_mut(&mut self) -> Box<dyn Iterator<Item = &mut Self::Item> + '_> {
Box::new(<&mut X as IntoIterator>::into_iter(self))
}
}
Loading

0 comments on commit 630e904

Please sign in to comment.