#2326·cocoindex

Rust SDK: gaps on processing components dispatching and target states declaration

Author: georgeh0Created Jul 27, 2026Updated Jul 28, 2026

See examples first

Rust example

e.g. code from the text_embedding example:

https://github.com/cocoindex-io/cocoindex/blob/dea6e6b92ba76519bb81e2b1d9509a990faf47b1/examples/rust/text_embedding/src/main.rs#L124-L139

  • It walks the target directory, collects all files first and keep them in a Vec.
  • After all files collected, it mounts a component for each file, and collect all result rows.
  • At last, it calls declare_row() for rows collected from all files at top level.

Corresponding Python example

Conversely, this is how the Python example works:

https://github.com/cocoindex-io/cocoindex/blob/dea6e6b92ba76519bb81e2b1d9509a990faf47b1/examples/text_embedding/main.py#L117-L123

  • It calls walk_dir to get a iterator, so mount_each() consumes files in streaming manner. We don't need to wait until all files collected in memory before starting doing work.

  • Each component to process each file doesn't return anything. So we don't need to load and collect target rows for unchanged files.

  • declare_row() is called within the component, i.e. these target rows are owned by components for each specific file. These data only need to be in memory and reconciled when the specific file changes.

    https://github.com/cocoindex-io/cocoindex/blob/dea6e6b92ba76519bb81e2b1d9509a990faf47b1/examples/text_embedding/main.py#L79-L88

The gaps

  1. Sources need to provide methods to return an iterator or stream. mount_each! needs to be able to take iterator/stream as input and dispatch works in streaming manner.
  2. In most cases we should call declare_* within specific components, and let the components return nothing, so unchanged components can be skipped cheaply and instantly (without loading anything in memory).
  3. Semantics of mount_each! should be based on mount() rather than use_mount(), i.e. it's "dispatching the component in background" (no return value dependency) rather than "running and wait for the result". It returns a handle similar to mount(), and the handle can be used to waiting for readiness optionally, but by default after it's dispatched we don't block on it. See Processing component for more explanations of the difference.