OneOrMany implemented in Rust

In the tuple |(d, embeddings)|, the components correspond to the values yielded by documents.into_iter()

  1. d
    • This represents the original Word struct that was passed to EmbeddingsBuilder.
    • Since document(Word { ... }) was used, d is an instance of Word, meaning it has fields like id and definition.
  2. embeddings
    • This represents the embeddings generated for d.
    • Since EmbeddingsBuilder likely produces a list of embeddings per document, embeddings is a vector (Vec) of embedding structures.
    • The .first().vec call suggests that each embedding in embeddings has a field .vec that contains the actual numerical embedding as a Vec<f64>.

Thus:

  • d → A Word struct (containing id and definition).
  • embeddings → A Vec<Embedding> where Embedding has a .vec field that holds the actual numerical embedding.

Example of OneOrMany Utility Type in Rust

use std::fmt::Debug;

// A utility enum to represent either one item or many items
#[derive(Debug)]
enum OneOrMany<T> {
    One(T),
    Many(Vec<T>),
}

impl<T> OneOrMany<T> {
    // Method to check if we have a single item
    fn is_one(&self) -> bool {
        matches!(self, OneOrMany::One(_))
    }

    // Method to get the first item (either from One or Many)
    fn first(&self) -> Option<&T> {
        match self {
            OneOrMany::One(ref item) => Some(item),
            OneOrMany::Many(ref items) => items.first(),
        }
    }

    // Method to map over the item(s) inside OneOrMany
    fn map<U, F>(self, f: F) -> OneOrMany<U>
    where
        F: Fn(T) -> U,
    {
        match self {
            OneOrMany::One(item) => OneOrMany::One(f(item)),
            OneOrMany::Many(items) => OneOrMany::Many(items.into_iter().map(f).collect()),
        }
    }
}

// Example usage with an Embedding struct
#[derive(Debug, Clone)]
struct Embedding {
    vec: Vec<f64>,
}

fn main() {
    // Example 1: Single Embedding
    let embedding_one = OneOrMany::One(Embedding {
        vec: vec![1.0, 2.0, 3.0],
    });

    // Example 2: Multiple Embeddings
    let embedding_many = OneOrMany::Many(vec![
        Embedding { vec: vec![1.0, 2.0, 3.0] },
        Embedding { vec: vec![4.0, 5.0, 6.0] },
    ]);

    // Using `first` to get the first embedding
    println!("First from one: {:?}", embedding_one.first());
    println!("First from many: {:?}", embedding_many.first());

    // Mapping over the items
    let transformed = embedding_many.map(|embedding| Embedding {
        vec: embedding.vec.iter().map(|x| x * 2.0).collect(),
    });

    println!("Transformed: {:?}", transformed);
}

Breakdown:

  1. OneOrMany<T> is an enum with two variants:
    • One(T): Holds a single item of type T.
    • Many(Vec<T>): Holds multiple items in a vector.
  2. Methods:
    • is_one: Checks if the enum is holding a single item.
    • first: Returns a reference to the first item if present.
    • map: Allows transforming the items inside the enum.
  3. Example Usage:
    • We create a OneOrMany<Embedding> for both a single embedding and multiple embeddings.
    • We demonstrate how .first() can be used to access the first element from either case.
    • We also show how .map() can be used to modify the values inside the enum.

Example Output:

First from one: Some(Embedding { vec: [1.0, 2.0, 3.0] })
First from many: Some(Embedding { vec: [1.0, 2.0, 3.0] })
Transformed: Many([Embedding { vec: [2.0, 4.0, 6.0] }, Embedding { vec: [8.0, 10.0, 12.0] }])

In this case, OneOrMany makes it flexible to handle cases where you might have a single embedding or multiple embeddings, while keeping the code concise and readable.

one-or-many
Rust Programming

Previous article

WASM in Rust