Golang Samber Ro
Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
samber/ro is a Go implementation of ReactiveX that provides a type-safe, declarative approach to managing asynchronous data streams. It replaces complex, manual goroutine and channel wiring with a composable pipeline architecture. The library features over 150 operators, support for both cold and hot observables, automatic backpressure, and built-in error propagation. It is designed for scenarios involving infinite event streams, real-time data processing, and complex async coordination, offering a robust alternative to standard library concurrency primitives when declarative stream management is required.
What it does
- Provides 150+ type-safe operators for transforming, filtering, and combining data streams.
- Supports cold and hot observables for flexible event distribution and resource management.
- Includes 40+ plugins for common tasks like HTTP, cron, logging, and file system monitoring.
- Implements automatic backpressure and error propagation across pipeline stages.
- Integrates natively with Go context for cancellation and timeout management.
- Offers five subject types for advanced multicast and state-sharing patterns.
When to use it
- When building infinite event-driven pipelines (e.g., WebSockets, tickers, file watchers).
- When real-time data enrichment requires combining multiple asynchronous sources.
- When you need declarative retry, timeout, or fallback logic for remote service calls.
- When managing pub/sub patterns where multiple consumers share a single data source.
- When standard goroutine/channel patterns become too verbose or difficult to maintain.
Advantages
- Reduces boilerplate code compared to manual channel and goroutine management.
- Enhances code readability through declarative, chainable pipeline operators.
- Improves reliability with built-in error handling and context-aware cancellation.
- Provides compile-time type safety via typed Pipe functions.
- Simplifies complex async coordination like stream merging, zipping, and throttling.
Usage examples
- “Create a reactive pipeline that filters even numbers from an interval stream and maps them to strings.”
- The agent generates a Pipe2 chain using ro.RangeWithInterval, ro.Filter, and ro.Map, followed by a Subscribe call to handle the output.
- “How do I implement a remote API call with retries and a timeout using samber/ro?”
- The agent provides a pipeline using ro.Timeout and ro.RetryWithConfig to wrap the API call, ensuring robust execution.
- “Show me how to share a single WebSocket stream between two different consumers.”
- The agent demonstrates using ro.Share to convert a cold observable into a hot one, allowing multiple subscribers to receive the same events.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/samber/golang-samber-ro/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip golang-samber-ro.zip -d .agents/skills
Documentation by samber
Persona: You are a Go engineer who reaches for reactive streams when data flows asynchronously or infinitely. You use samber/ro to build declarative pipelines instead of manual goroutine/channel wiring, but you know when a simple slice + samber/lo is enough.
Thinking mode: Reason as thoroughly as possible when designing advanced reactive pipelines or choosing between cold/hot observables, subjects, and combining operators — wrong architecture leads to resource leaks or missed events. On Claude Code, use ultrathink to trigger extended thinking explicitly.
samber/ro — Reactive Streams for Go
Go implementation of ReactiveX. Generics-first, type-safe, composable pipelines for asynchronous data streams with automatic backpressure, error propagation, context integration, and resource cleanup. 150+ operators, 5 subject types, 40+ plugins.
Official Resources:
This skill is not exhaustive — refer to library documentation and code examples for more information:
For Go package docs, symbols, versions, importers, and known vulnerabilities, → See
samber/cc-skills-golang@golang-pkg-go-devskill (godig), preferred over Context7 for Go package facts.To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See
samber/cc-skills-golang@golang-goplsskill (gopls).Context7 remains a fallback for docs not indexed on pkg.go.dev.
Why samber/ro (Streams vs Slices)
Go channels + goroutines become unwieldy for complex async pipelines: manual channel closures, verbose goroutine lifecycle, error propagation across nested selects, and no composable operators. samber/ro solves this with declarative, chainable stream operators.
When to use which tool:
| Scenario | Tool | Why |
|---|---|---|
| Transform a slice (map, filter, reduce) | samber/lo | Finite, synchronous, eager — no stream overhead needed |
| Simple goroutine fan-out with error handling | errgroup | Standard lib, lightweight, sufficient for bounded concurrency |
| Infinite event stream (WebSocket, tickers, file watcher) | samber/ro | Declarative pipeline with backpressure, retry, timeout, combine |
| Real-time data enrichment from multiple async sources | samber/ro | CombineLatest/Zip compose dependent streams without manual select |
| Pub/sub with multiple consumers sharing one source | samber/ro | Hot observables (Share/Subjects) handle multicast natively |
Key differences: lo vs ro
| Aspect | samber/lo | samber/ro |
|---|---|---|
| Data | Finite slices | Infinite streams |
| Execution | Synchronous, blocking | Asynchronous, non-blocking |
| Evaluation | Eager (allocates intermediate slices) | Lazy (processes items as they arrive) |
| Timing | Immediate | Time-aware (delay, throttle, interval, timeout) |
| Error model | Return (T, error) per call | Error channel propagates through pipeline |
| Use case | Collection transforms | Event-driven, real-time, async pipelines |
Installation
go get github.com/samber/roCore Concepts
Four building blocks:
Observable — a data source that emits values over time. Cold by default: each subscriber triggers independent execution from scratch
Observer — a consumer with three callbacks:
onNext(T),onError(error),onComplete()Operator — a function that transforms an observable into another observable, chained via
PipeSubscription — the connection between observable and observer. Call
.Wait()to block or.Unsubscribe()to cancel
observable := ro.Pipe2(
ro.RangeWithInterval(0, 5, 1*time.Second),
ro.Filter(func(x int) bool { return x%2 == 0 }),
ro.Map(func(x int) string { return fmt.Sprintf("even-%d", x) }),
)
observable.Subscribe(ro.NewObserver(
func(s string) { fmt.Println(s) }, // onNext
func(err error) { log.Println(err) }, // onError
func() { fmt.Println("Done!") }, // onComplete
))
// Output: "even-0", "even-2", "even-4", "Done!"
// Or collect synchronously:
values, err := ro.Collect(observable)Cold vs Hot Observables
Cold (default): each .Subscribe() starts a new independent execution. Safe and predictable — use by default.
Hot: multiple subscribers share a single execution. Use when the source is expensive (WebSocket, DB poll) or subscribers must see the same events.
| Convert with | Behavior |
|---|---|
Share() | Cold → hot with reference counting. Last unsubscribe tears down |
ShareReplay(n) | Same as Share + buffers last N values for late subscribers |
Connectable() | Cold → hot, but waits for explicit .Connect() call |
| Subjects | Natively hot — call .Send(), .Error(), .Complete() directly |
| Subject | Constructor | Replay behavior |
|---|---|---|
PublishSubject | NewPublishSubject[T]() | None — late subscribers miss past events |
BehaviorSubject | NewBehaviorSubject[T](initial) | Replays last value to new subscribers |
ReplaySubject | NewReplaySubject[T](bufferSize) | Replays last N values |
AsyncSubject | NewAsyncSubject[T]() | Emits only last value, only on complete |
UnicastSubject | NewUnicastSubject[T](bufferSize) | Single subscriber only |
For subject details and hot observable patterns, see Subjects Guide.
Operator Quick Reference
| Category | Key operators | Purpose |
|---|---|---|
| Creation | Just, FromSlice, FromChannel, Range, Interval, Defer, Future | Create observables from various sources |
| Transform | Map, MapErr, FlatMap, Scan, Reduce, GroupBy | Transform or accumulate stream values |
| Filter | Filter, Take, TakeLast, Skip, Distinct, Find, First, Last | Selectively emit values |
| Combine | Merge, Concat, Zip2–Zip6, CombineLatest2–CombineLatest5, Race | Merge multiple observables |
| Error | Catch, OnErrorReturn, OnErrorResumeNextWith, Retry, RetryWithConfig | Recover from errors |
| Timing | Delay, DelayEach, Timeout, ThrottleTime, SampleTime, BufferWithTime | Control emission timing |
| Side effect | Tap/Do, TapOnNext, TapOnError, TapOnComplete | Observe without altering stream |
| Terminal | Collect, ToSlice, ToChannel, ToMap | Consume stream into Go types |
Use typed Pipe2, Pipe3 ... Pipe25 for compile-time type safety across operator chains. The untyped Pipe uses any and loses type checking.
For the complete operator catalog (150+ operators with signatures), see Operators Guide.
Common Mistakes
| Mistake | Why it fails | Fix |
|---|---|---|
Using ro.OnNext() without error handler | Errors are silently dropped — bugs hide in production | Use ro.NewObserver(onNext, onError, onComplete) with all 3 callbacks |
Using untyped Pipe() instead of Pipe2/Pipe3 | Loses compile-time type safety, errors surface at runtime | Use Pipe2, Pipe3...Pipe25 for typed operator chains |
Forgetting .Unsubscribe() on infinite streams | Goroutine leak — the observable runs forever | Use TakeUntil(signal), context cancellation, or explicit Unsubscribe() |
Using Share() when cold is sufficient | Unnecessary complexity, harder to reason about lifecycle | Use hot observables only when multiple consumers need the same stream |
Using samber/ro for finite slice transforms | Stream overhead (goroutines, subscriptions) for a synchronous operation | Use samber/lo — it's simpler, faster, and purpose-built for slices |
| Not propagating context for cancellation | Streams ignore shutdown signals, causing resource leaks on termination | Chain ContextWithTimeout or ThrowOnContextCancel in the pipeline |
Best Practices
Always handle all three events — use
NewObserver(onNext, onError, onComplete), not justOnNext. Unhandled errors cause silent data lossUse
Collect()for synchronous consumption — when the stream is finite and you need[]T,Collectblocks until complete and returns the slice + errorPrefer typed Pipe functions —
Pipe2,Pipe3...Pipe25catch type mismatches at compile time. Reserve untypedPipefor dynamic operator chainsBound infinite streams — use
Take(n),TakeUntil(signal),Timeout(d), or context cancellation. Unbounded streams leak goroutinesUse
Tap/Dofor observability — log, trace, or meter emissions without altering the stream. ChainTapOnErrorfor error monitoringPrefer
samber/lofor simple transforms — if the data is a finite slice and you need Map/Filter/Reduce, uselo. Reach forrowhen data arrives over time, from multiple sources, or needs retry/timeout/backpressure
Plugin Ecosystem
40+ plugins extend ro with domain-specific operators:
| Category | Plugins | Import path prefix |
|---|---|---|
| Encoding | JSON, CSV, Base64, Gob | plugins/encoding/... |
| Network | HTTP, I/O, FSNotify | plugins/http, plugins/io, plugins/fsnotify |
| Scheduling | Cron, ICS | plugins/cron, plugins/ics |
| Observability | Zap, Slog, Zerolog, Logrus, Sentry, Oops | plugins/observability/..., plugins/samber/oops |
| Rate limiting | Native, Ulule | plugins/ratelimit/... |
| Data | Bytes, Strings, Sort, Strconv, Regexp, Template | plugins/bytes, plugins/strings, etc. |
| System | Process, Signal | plugins/proc, plugins/signal |
For the full plugin catalog with import paths and usage examples, see Plugin Ecosystem.
For real-world reactive patterns (retry+timeout, WebSocket fan-out, graceful shutdown, stream combination), see Patterns.
If you encounter a bug or unexpected behavior in samber/ro, open an issue at github.com/samber/ro/issues.
Cross-References
→ See
samber/cc-skills-golang@golang-samber-loskill for finite slice transforms (Map, Filter, Reduce, GroupBy) — use lo when data is already in a slice→ See
samber/cc-skills-golang@golang-samber-moskill for monadic types (Option, Result, Either) that compose with ro pipelines→ See
samber/cc-skills-golang@golang-samber-hotskill for in-memory caching (also available as an ro plugin)→ See
samber/cc-skills-golang@golang-concurrencyskill for goroutine/channel patterns when reactive streams are overkill→ See
samber/cc-skills-golang@golang-observabilityskill for monitoring reactive pipelines in production
Files
- ATTRIBUTION.mdAdded by AutoGineer905 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md11.8 KiB
- evals/evals.json17.3 KiB
- references/operators-guide.md14.6 KiB
- references/patterns.md8.0 KiB
- references/plugin-ecosystem.md6.3 KiB
- references/subjects-guide.md6.3 KiB
references/operators-guide.md
Operators Guide
samber/ro provides 150+ operators organized by category. All operators are generic functions that take and return Observable[T], designed for chaining via Pipe.
Table of Contents
Pipeline Construction
Creation Operators
Transformation Operators
Filtering Operators
Combining Operators
Math and Aggregation
Error Handling
Timing and Buffering
Side Effects (Tap / Do)
Connectable and Sharing
Context Operators
Conditional Operators
Terminal Operators
Observer and Subscription
Scheduling
Concurrency Modes
Pipeline Construction
// Typed pipes (Pipe1 through Pipe25) — compile-time type safety
result := ro.Pipe2(source, op1, op2)
result := ro.Pipe3(source, op1, op2, op3)
// Untyped pipe — uses any, loses type checking
result := ro.Pipe(source, op1, op2, op3)
// Reusable operator composition (curried)
transform := ro.PipeOp2(op1, op2) // returns func(Observable[A]) Observable[C]
result := transform(source)
// Synchronous collection
values, err := ro.Collect(observable)Creation Operators
Create observables from various sources. Typically the first argument to Pipe.
| Operator | Signature | Purpose |
|---|---|---|
Just | Just[T](values ...T) Observable[T] | Emit specific values then complete |
Of | Of[T](values ...T) Observable[T] | Alias for Just |
FromSlice | FromSlice[T](items []T) Observable[T] | Create from existing slice |
FromChannel | FromChannel[T](ch <-chan T) Observable[T] | Wrap Go channel as observable |
Range | Range(start, end int) Observable[int] | Emit integer sequence |
RangeWithStep | RangeWithStep(start, end, step int) Observable[int] | Integer sequence with custom step |
RangeWithInterval | RangeWithInterval(start, end int, d time.Duration) Observable[int] | Integers with delay between each |
RangeWithStepAndInterval | RangeWithStepAndInterval(start, end, step int, d time.Duration) Observable[int] | Step + interval combined |
Interval | Interval(d time.Duration) Observable[int64] | Emit sequential integers at intervals (infinite) |
IntervalWithInitial | IntervalWithInitial(initial, d time.Duration) Observable[int64] | Interval with initial delay |
Timer | Timer(d time.Duration) Observable[int64] | Single emission after delay |
Repeat | Repeat[T](item T, count int64) Observable[T] | Repeat item N times |
RepeatWithInterval | RepeatWithInterval[T](item T, count int64, d time.Duration) Observable[T] | Repeat with delay |
Defer | Defer[T](factory func() Observable[T]) Observable[T] | Lazily create observable on subscription |
Future | Future[T](factory func() (T, error)) Observable[T] | Single async value from function |
Start | Start(cb func() (any, error)) Observable[any] | Execute callback and emit result |
Empty | Empty[T]() Observable[T] | Complete immediately, no values |
Never | Never[T]() Observable[T] | Never emit or complete |
Throw | Throw[T](err error) Observable[T] | Immediately error |
// Custom observable with direct control
obs := ro.NewObservable[int](func(ctx context.Context, observer ro.Observer[int]) error {
observer.Next(1)
observer.Next(2)
observer.Complete()
return nil
})Transformation Operators
Transform each value in the stream.
| Operator | Purpose |
|---|---|
Map[T, R](fn func(T) R) | Transform each value T -> R |
MapI[T, R](fn func(T, int64) R) | Map with index |
MapErr[T, R](fn func(T) (R, error)) | Map that can fail — error propagates |
MapWithContext[T, R](fn func(ctx, T) (ctx, R)) | Map with context access |
MapTo[T, R](output R) | Replace all values with a constant |
FlatMap[T, R](fn func(T) Observable[R]) | Map each value to observable, flatten results |
MergeMap[T, R](fn func(T) Observable[R]) | Alias for FlatMap |
Scan[T, R](fn func(R, T) R, seed R) | Running accumulation, emit each intermediate |
Reduce[T, R](fn func(R, T) R, seed R) | Accumulate, emit only final result |
GroupBy[T, K](fn func(T) K) | Partition into grouped observables by key |
Cast[T, U]() | Type cast values |
Flatten[T]() | Flatten Observable[[]T] to Observable[T] |
Materialize[T]() | Wrap values in Notification[T] (next/error/complete) |
Dematerialize[T]() | Unwrap Notification[T] back to values |
Timestamp[T]() | Emit TimestampValue[T] with emission time |
TimeInterval[T]() | Emit IntervalValue[T] with time since last emission |
// FlatMap: for each user ID, fetch their orders (returns observable)
orders := ro.Pipe2(
userIDs,
ro.FlatMap(func(id int) ro.Observable[Order] {
return fetchOrders(id)
}),
ro.Filter(func(o Order) bool { return o.Status == "paid" }),
)
// Scan: running sum
ro.Pipe1(
ro.Just(1, 2, 3, 4, 5),
ro.Scan(func(acc, x int) int { return acc + x }, 0),
)
// Emits: 1, 3, 6, 10, 15Filtering Operators
Selectively emit values from the stream.
| Operator | Purpose |
|---|---|
Filter[T](fn func(T) bool) | Emit only values matching predicate |
FilterI[T](fn func(T, int64) bool) | Filter with index |
FilterWithContext[T](fn func(ctx, T) (ctx, bool)) | Filter with context access |
Distinct[T comparable]() | Emit only unique values |
DistinctBy[T, K](fn func(T) K) | Unique by key function |
Take[T](n int64) | Emit first N values then complete |
TakeLast[T](n int) | Emit last N values |
TakeWhile[T](fn func(T) bool) | Emit while predicate true, then complete |
TakeUntil[T, S](signal Observable[S]) | Emit until signal observable emits |
Skip[T](n int64) | Skip first N values |
SkipLast[T](n int) | Skip last N values |
SkipWhile[T](fn func(T) bool) | Skip while predicate true |
SkipUntil[T, S](signal Observable[S]) | Skip until signal emits |
Head[T]() | First item only |
Tail[T]() | All items except first |
ElementAt[T](n int) | Item at specific index |
ElementAtOrDefault[T](n int64, fallback T) | Item at index or default |
Find[T](fn func(T) bool) | First value matching predicate |
First[T](fn func(T) bool) | First matching value (alias-like) |
Last[T](fn func(T) bool) | Last matching value |
Contains[T](fn func(T) bool) | Emit bool: whether any value matches |
All filtering operators have I (indexed), WithContext, and IWithContext variants.
Combining Operators
Merge multiple observables into one.
| Operator | Purpose |
|---|---|
Merge[T](sources ...Observable[T]) | Interleave emissions from all sources |
MergeWith[T](obs ...Observable[T]) | Chainable merge |
MergeAll[T]() | Flatten Observable[Observable[T]] by merging |
Concat[T](obs ...Observable[T]) | Sequential: complete first, then start second |
ConcatWith[T](obs ...Observable[T]) | Chainable concat |
ConcatAll[T]() | Flatten by concatenating sequentially |
Zip2[A, B](a, b) | Pair values from 2 sources into lo.Tuple2 |
Zip3 ... Zip6 | Zip 3-6 sources into corresponding lo.Tuple |
ZipWith[A, B](obsB) | Chainable zip |
CombineLatest2[A, B](a, b) | Emit combined latest when either source emits |
CombineLatest3 ... CombineLatest5 | Combine 3-5 sources |
CombineLatestWith[A, B](obsB) | Chainable combine-latest |
Race[T](sources ...Observable[T]) | Emit from whichever source emits first |
Amb[T](sources ...) | Alias for Race |
StartWith[T](prefixes ...T) | Prepend values before source emissions |
EndWith[T](suffixes ...T) | Append values after source completes |
// Zip: pair user with their settings
ro.Zip2(userStream, settingsStream)
// Emits: lo.Tuple2[User, Settings]
// CombineLatest: re-emit whenever either changes
ro.CombineLatest2(priceStream, quantityStream)
// Emits latest (price, quantity) pair each time either updatesMath and Aggregation
| Operator | Purpose |
|---|---|
Count[T]() | Count of emitted values |
Sum[T Numeric]() | Sum of all values |
Average[T Numeric]() | Average as float64 |
Max[T Numeric]() | Maximum value |
Min[T Numeric]() | Minimum value |
Abs() | Absolute value (float64) |
Ceil() / Floor() / Round() / Trunc() | Rounding (float64) |
CeilWithPrecision(n) / FloorWithPrecision(n) | Rounding with decimal places |
Clamp[T](lower, upper) | Constrain values to range |
Error Handling
| Operator | Purpose |
|---|---|
Catch[T](fn func(error) Observable[T]) | Catch error, switch to recovery observable |
OnErrorReturn[T](value T) | Replace error with fallback value |
OnErrorResumeNextWith[T](obs ...Observable[T]) | Continue with fallback observables on error |
Retry[T]() | Retry indefinitely on error |
RetryWithConfig[T](cfg RetryConfig) | Retry with max attempts, delay, backoff |
ThrowIfEmpty[T](fn func() error) | Error if stream completes empty |
// RetryConfig for exponential backoff
ro.RetryWithConfig[Response](ro.RetryConfig{
Max: 3,
Delay: time.Second,
BackoffMultiplier: 2.0,
MaxDelay: 10 * time.Second,
})Timing and Buffering
| Operator | Purpose |
|---|---|
Delay[T](d time.Duration) | Delay entire stream |
DelayEach[T](d time.Duration) | Delay between each emission |
Timeout[T](d time.Duration) | Error if no emission within duration |
ThrottleTime[T](d time.Duration) | Ignore values within duration of last |
ThrottleWhen[T, t](tick Observable[t]) | Throttle by signal |
SampleTime[T](d time.Duration) | Emit latest value at intervals |
SampleWhen[T, t](tick Observable[t]) | Sample by signal |
BufferWithCount[T](n int) | Collect N items, emit as []T |
BufferWithTime[T](d time.Duration) | Collect within time window |
BufferWithTimeOrCount[T](n, d) | Buffer with either condition |
BufferWhen[T, B](boundary Observable[B]) | Buffer until signal |
WindowWhen[T, B](boundary Observable[B]) | Window as nested observables |
Pairwise[T]() | Emit consecutive pairs |
Side Effects (Tap / Do)
Execute code without changing the stream. Do is an alias for Tap.
| Operator | Purpose |
|---|---|
Tap[T](onNext, onError, onComplete) | Side effect on all events |
TapOnNext[T](fn func(T)) | Side effect on values |
TapOnError[T](fn func(error)) | Side effect on errors |
TapOnComplete[T](fn func()) | Side effect on completion |
TapOnSubscribe[T](fn func()) | Side effect on subscription |
TapOnFinalize[T](fn func()) | Side effect on teardown |
All have WithContext variants. Do, DoOnNext, DoOnError, DoOnComplete, DoOnSubscribe, DoOnFinalize are aliases.
Connectable and Sharing
| Operator | Purpose |
|---|---|
Share[T]() | Cold -> hot with reference counting |
ShareWithConfig[T](cfg ShareConfig[T]) | Share with reset options |
ShareReplay[T](bufferSize int) | Share + replay last N values to late subscribers |
ShareReplayWithConfig[T](n, cfg) | ShareReplay with reset options |
Serialize[T]() | Queue emissions to ensure serial delivery |
// ShareConfig controls lifecycle reset
ro.ShareConfig[T]{
ResetOnComplete: true, // re-subscribe on complete
ResetOnError: true, // re-subscribe on error
ResetOnReferenceCount: true, // re-subscribe when count drops to 0
}Context Operators
| Operator | Purpose |
|---|---|
ContextReset[T](ctx context.Context) | Replace pipeline context |
ContextWithValue[T](key, value any) | Add value to context |
ContextWithTimeout[T](d time.Duration) | Add timeout |
ContextWithDeadline[T](t time.Time) | Add deadline |
ContextMap[T](fn func(ctx) ctx) | Transform context |
ThrowOnContextCancel[T]() | Error when context is cancelled |
Conditional Operators
| Operator | Purpose |
|---|---|
All[T](fn func(T) bool) | Emit bool: whether all values match |
DefaultIfEmpty[T](value T) | Emit default if source completes empty |
ThrowIfEmpty[T](fn func() error) | Error if empty |
Iif[T](pred func() bool, a, b Observable[T]) | Choose between two observables |
While[T](cond func() bool) | Repeat while condition true |
DoWhile[T](cond func() bool) | Execute at least once, repeat while true |
SequenceEqual[T](obsB Observable[T]) | Emit bool: whether two streams match |
Terminal Operators
| Operator | Purpose |
|---|---|
Collect[T](obs Observable[T]) ([]T, error) | Block, return all values as slice |
CollectWithContext[T](ctx, obs) ([]T, ctx, error) | Collect with context |
ToSlice[T]() | Operator: emit []T on complete |
ToChannel[T](size int) | Convert to <-chan Notification[T] |
ToMap[T, K, V](fn func(T) (K, V)) | Collect into map |
Observer and Subscription
// Full observer (recommended)
observer := ro.NewObserver[T](onNext, onError, onComplete)
// Context-aware observer
observer := ro.NewObserverWithContext[T](onNext, onError, onComplete)
// Convenience shortcuts
observer := ro.OnNext[T](func(v T) { ... })
observer := ro.PrintObserver[T]() // debug: prints all events
observer := ro.NoopObserver[T]() // discard all events
// Subscription lifecycle
sub := observable.Subscribe(observer)
sub.Wait() // block until complete or error
sub.Unsubscribe() // cancel and cleanup
sub.IsActive() // check if still running
sub.GetError() // get terminal errorScheduling
| Operator | Purpose |
|---|---|
SubscribeOn[T](bufferSize int) | Run subscription on async scheduler |
ObserveOn[T](bufferSize int) | Deliver notifications on async scheduler |
Concurrency Modes
// Safe (default): synchronized emissions
ro.NewObservable[T](fn)
ro.NewSafeObservable[T](fn)
// Unsafe: no synchronization, caller must guarantee single-goroutine access
ro.NewUnsafeObservable[T](fn)
// Eventually safe: allows brief unsynchronized period, then synchronizes
ro.NewEventuallySafeObservable[T](fn)Requirements and compatibility
Written for: Claude Code, OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Claude Code frontmatter
The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- samber
- Publisher
- Samuel Berthe (Community)
- Distributed by
- Samuel Berthe
- Source
- samber/cc-skills-golang · skills/golang-samber-ro · commit bb1d43d
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-samber-ro.zip