ibapi 4.0.1

A Rust implementation of the Interactive Brokers TWS API, providing a reliable and user friendly interface for TWS and IB Gateway. Designed with a focus on simplicity and performance.
Documentation
---
id: dual-feature-types
title: Dual-feature public types use per-feature submodules and an async-preferring alias
cluster: parity
status: active
triggers:
  - adding a public type with distinct sync and async impls
  - two same-name structs collide under --all-features
  - writing a trait method or Client method that returns a per-feature type
  - a path containing the async keyword fails to parse
symbols: [NoticeStream, sync_impl, async_impl, Subscription, r#async]
related: [feature-matrix, no-parity-wrappers, subscription-consumer-idiom]
precedents: ["#512", "#526"]
memory: [feedback_raw_identifier_async_path, feedback_plan_cfg_receiver_types]
---

A public type with distinct sync/async impls — different receivers, different async-ness —
follows the `NoticeStream` pattern in `src/subscriptions/notice_stream.rs`:

1. Two submodules in one file: `pub mod sync_impl` and `pub mod async_impl`.
2. A top-level alias that **prefers async** when both features are on:
   ```rust
   #[cfg(feature = "async")]
   pub use notice_stream::async_impl::NoticeStream;
   #[cfg(all(feature = "sync", not(feature = "async")))]
   pub use notice_stream::sync_impl::NoticeStream;
   ```
3. The sync version also re-exported at `client::blocking::*`, so a both-features build can
   still name it.

Trait method return types and `Client::*` signatures must spell out the full per-feature path
(`crate::subscriptions::notice_stream::async_impl::NoticeStream`), not the alias.

## Why

Naive same-name sibling structs gated `#[cfg(feature = "sync")]` / `#[cfg(feature = "async")]`
compile fine in each single-feature build and then collide under `--all-features`. The alias
indirection is what lets both types exist at once while one name stays canonical, so
`cargo check --all-features` is the check that matters here — see
[feature matrix](feature-matrix.md), whose CI legs do not cover it.

`async` is a Rust keyword, so any path crossing that module needs the raw identifier:
`crate::subscriptions::r#async::Subscription`. Plain `async::` is a parse error, not a
resolution error — the compiler message points at syntax and reads as unrelated.

Two `#[cfg]`-gated impl blocks on **different receiver types** coexist legally; they are not
mutually exclusive alternatives. Plans that assume otherwise call for needless
feature-splitting, so state the receiver types explicitly when scoping this work.

`Subscription` predates the convention: same dual-export idea, but flat
`subscriptions/sync.rs` + `subscriptions/async.rs` files instead of `sync_impl` / `async_impl`
submodules. Don't copy that file layout for new types.

## Precedents

- #512 — introduced the global `NoticeStream` for unrouted IB notices, establishing the
  per-feature submodule split.
- #526 — unified the notice API on `Client::builder()`, which is where the async-preferring
  alias and the `client::blocking::*` mirror settled into their current shape.