Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go Bindings

ros-z-go lets Go applications communicate with ROS 2 and Rust nodes over the same Zenoh transport. It uses CGO to call the Rust FFI layer and exposes an idiomatic builder-pattern API.

Tip

New here? Start with Quick Start to get a publisher and subscriber running in five minutes.


Architecture

graph TD
    A[Go Code] -->|CGO| B[C FFI Layer]
    B --> C[Rust ros-z]
    C -->|Zenoh| D[Network]
    D -->|Zenoh| E[ROS 2 / Rust / Python Nodes]

Three layers:

LayerLocationRole
Gorosz/ packageIdiomatic API, builder pattern
C FFIros_z_ffi.hAuto-generated by cbindgen
Rustffi/ moduleBridges to ros-z core via Zenoh

Callbacks flow in reverse: Rust invokes C function pointers → dispatched to Go via //export functions.


Project Setup

Add to your go.mod:

require github.com/ZettaScaleLabs/ros-z/crates/ros-z-go v0.0.0
replace github.com/ZettaScaleLabs/ros-z/crates/ros-z-go => /path/to/ros-z/crates/ros-z-go

The #cgo LDFLAGS in rosz/context.go resolves the library via ${SRCDIR} — no extra CGO_LDFLAGS needed with a replace directive.

Generate message types (no ROS 2 install needed for bundled types):

just -f crates/ros-z-go/justfile codegen-bundled   # std_msgs, geometry_msgs
just -f crates/ros-z-go/justfile codegen            # full set from a ROS 2 installation

Context

ctx, err := rosz.NewContext().WithDomainID(0).Build()
defer ctx.Close()

For cloud, Docker, or multi-machine deployments:

ctx, err := rosz.NewContext().
    WithMode(rosz.ModeClient).
    WithConnectEndpoints("tcp/192.168.1.100:7447").
    DisableMulticastScouting().
    Build()
OptionDescription
WithDomainID(n)ROS domain ID (0–232)
WithMode(mode)ModePeer (default) · ModeClient · ModeRouter
WithConnectEndpoints(ep...)Explicit router addresses
DisableMulticastScouting()Required in Docker / cloud
ConnectToLocalZenohd()Shorthand for tcp/127.0.0.1:7447
WithConfigFile(path)Zenoh JSON5 config file
WithJSON(str)Inline Zenoh config as dotted-key JSON
WithRemapRules(rules...)Topic/service remapping (from:=to)
WithLogging()Initialise Zenoh tracing

Node

node, err := ctx.CreateNode("talker").Build()
defer node.Close()

// Namespaced — all topics/services are prefixed
node, err := ctx.CreateNode("talker").WithNamespace("/robot").Build()

// Enable type description service (makes `ros2 topic info --verbose` work)
node, err := ctx.CreateNode("talker").WithTypeDescriptionService().Build()

Pub / Sub

// Publisher
pub, err := node.CreatePublisher("chatter").Build(&std_msgs.String{})
pub.Publish(&std_msgs.String{Data: "hello"})

// Subscriber — callback on Rust thread (keep it short)
sub, err := node.CreateSubscriber("chatter").
    BuildWithCallback(&std_msgs.String{}, func(data []byte) {
        msg := &std_msgs.String{}
        msg.DeserializeCDR(data)
        log.Println(msg.Data)
    })

Apply QoS with .WithQoS(rosz.QosSensorData()) on either builder.


Services

Coming in v0.2. Service client and server support will be added in the next release.


Actions

Coming in v0.2. Action client and server support, including cooperative cancellation and feedback streaming, will be added in the next release.


Typed API

The typed helpers eliminate manual SerializeCDR / DeserializeCDR calls.

Subscriber

// Typed callback
sub, err := rosz.BuildWithTypedCallback(
    node.CreateSubscriber("chatter"),
    func(msg *std_msgs.String) { log.Println(msg.Data) },
)

// Channel — range over messages
sub, ch, cleanup, err := rosz.SubscriberWithChannel[*std_msgs.String](
    node.CreateSubscriber("chatter"), 10 /* buffer */)
defer cleanup(); defer sub.Close()
for msg := range ch { log.Println(msg.Data) }

Use rosz.NewRingChannel (drops oldest on full) or rosz.NewFifoChannel (blocks on full) with SubscriberWithHandler for explicit buffering control.

Service

Coming in v0.2.

Action

Coming in v0.2.


QoS

rosz.QosDefault()          // Reliable, Volatile, KeepLast(10)
rosz.QosSensorData()       // BestEffort, Volatile, KeepLast(5) — high-rate streams
rosz.QosTransientLocal()   // Reliable, TransientLocal, KeepLast(1) — /tf_static, /robot_description
rosz.QosKeepAll()          // Reliable, Volatile, KeepAll

Apply with .WithQoS(profile) on the publisher or subscriber builder.

Tip

Match QoS profiles on both sides. BestEffort publisher + Reliable subscriber will not exchange messages.


Graph Introspection

topics, _ := node.GetTopicNamesAndTypes()    // []TopicInfo
names, _  := node.GetNodeNames()             // []NodeInfo
exists, _ := node.NodeExists("talker", "/")

Requires a Zenoh router and a brief settling time after nodes come online.


Error Handling

All Build() calls and operations return error. Use errors.Is() with sentinel values:

SentinelWhen raised
rosz.ErrBuildFailedBuild() failed — FFI returned nil
if errors.Is(err, rosz.ErrBuildFailed) { /* construction failed */ }

Inspect the error code directly when you need fine-grained handling:

var e rosz.RoszError
if errors.As(err, &e) {
    log.Fatalf("FFI error %d: %s", e.Code(), e.Message())
}

Warning

Callbacks are invoked on C/Rust threads. Do not call log.Fatal or block inside them — send to a channel or set an atomic flag instead.


Logging & Debugging

Set ROSZ_LOG to control the rosz package log level:

ROSZ_LOG=debug go run main.go   # all callbacks, CDR lengths, FFI calls
ROSZ_LOG=info  go run main.go   # lifecycle events
ROSZ_LOG=warn  go run main.go   # default — silent in production
ROSZ_LOG=error go run main.go   # errors only

Logs go to stderr via Go's log/slog structured text format. For Zenoh-level tracing add WithLogging() to the context builder and set RUST_LOG=zenoh=debug.


Examples

ExampleDemonstrates
publisher/Basic publish loop
subscriber/Typed callback subscriber
subscriber_channel/Channel-based, range-friendly
just -f crates/ros-z-go/justfile run-example <name>

Further Reading