Пример
tower-service 0.3.3: Allow generic Service dispatch and request pipelining via &mut S blanket impl without tying the returned Future to the reference lifetime
Проверенный пример — cargo tower-service 0.3.3: Allow generic Service dispatch and request pipelining via &mut S blanket impl without tying the returned…
sha256:78b5761fe34700285b0b8908344c8a23d8055a321ae72d6d0c76f55e6d77e78a
Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла.
Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди.
MIT-0
Свидетельства выполнения
Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.
- Основа свидетельства
- Подписанный контракт пройден
- Квитанции проверки
- 2
- Ключи подписи, собравшие его
- 2
Заявленная среда
rust linux x64 rust rust cargo
Среды запусков проверки
| Окружение | Контракт | Этапы | Запуск |
|---|---|---|---|
| rust 1 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · cargo@1 |
2026-08-16 |
| rust 1 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · cargo@1 |
2026-08-18 |
Кейс
HOW- Цель
- Allow generic Service dispatch and request pipelining via &mut S blanket impl without tying the returned Future to the reference lifetime
- Пакеты
- Символы
-
- tower_service::Service
- tower_service::Service::poll_ready
- tower_service::Service::call
- Окружение
- rust
- Создан
- 2026-08-16T11:21:18Z
Контракт
- Passing a mutable reference &mut S into a generic Service consumer yields S::Future without tying the future to the &mut borrow lifetime, allowing immediate re-borrowing to poll and dispatch subsequent requests while earlier futures remain in flight
- Box<dyn Service<Request, Response = Res, Error = Err, Future = Fut>> implements Service directly via blanket impl, requiring the Future associated type to be explicitly defined for trait object safety
- Each poll_ready readiness permit is single-use and consumed by the subsequent call, delegating transparently through &mut S and Box<S> blanket implementations
Файлы
- Cargo.lock
- Cargo.toml
- NOTES.md
- csx.json
- src/lib.rs
- tests/contract.rs
Исходный код
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "cargo-tower-service-api"
version = "0.1.0"
dependencies = [
"tower-service",
]
[[package]]
name = "tower-service"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[package]
name = "cargo-tower-service-api"
version = "0.1.0"
edition = "2021"
[dependencies]
tower-service = "0.3.3"
# Tower Service Trait Semantics and Blanket Implementations
## Search Result
`search_known_solution` returned existing samples focusing on high-level `tower` crate middleware combinators (`sha256:37636c110523173cb669e2b2a52feda3274b7cc8998bcb54c04b6b7a5c230bae` for `Buffer` and `ServiceBuilder` ordering; `sha256:70cb8cec09e074888556da7097e94d68ad979b471ca30473f3e2662c391dd7fd` for clone invalidation and queue interactions). This sample targets the foundational `tower-service` crate trait contracts: `&mut S` reference delegation and trait object dynamic dispatch.
## What a Model Would Have Written Instead
A naive model expects that passing a mutable reference `&mut S` into a generic function requiring `S: Service<Request>` will tie the returned `Future` to the lifetime of the `&mut S` borrow, preventing concurrent in-flight requests on an unbuffered service instance, or expects `Box<dyn Service<Request, Response = R, Error = E>>` to compile without specifying the `Future` associated type.
## How the Wrong Version Fails
Loudly with compile error `error[E0191]: the value of the associated type 'Future' (in trait 'tower_service::Service') must be specified` for omitted `Future` associated types in trait objects, or runtime assertion failures if a service is called without a preceding `poll_ready` permit.
{"case":{"believed":"Calling Service::call borrows \u0026mut self for the entire lifetime of the returned Future, preventing a single unbuffered service instance from polling and dispatching subsequent requests while a prior request is in flight.","caseId":"case:sha256:df2d3631d6a9367c247cd60935247308d7bb8e851bacaeea13f02e8a67e3d195","contract":["Passing a mutable reference \u0026mut S into a generic Service consumer yields S::Future without tying the future to the \u0026mut borrow lifetime, allowing immediate re-borrowing to poll and dispatch subsequent requests while earlier futures remain in flight","Box\u003cdyn Service\u003cRequest, Response = Res, Error = Err, Future = Fut\u003e\u003e implements Service directly via blanket impl, requiring the Future associated type to be explicitly defined for trait object safety","Each poll_ready readiness permit is single-use and consumed by the subsequent call, delegating transparently through \u0026mut S and Box\u003cS\u003e blanket implementations"],"goal":"Allow generic Service dispatch and request pipelining via \u0026mut S blanket impl without tying the returned Future to the reference lifetime","kind":"HOW","packages":["pkg:cargo/tower-service@0.3.3"],"schemaVersion":1,"symbols":["tower_service::Service","tower_service::Service::poll_ready","tower_service::Service::call"]},"contractCommand":["cargo","test","--offline"],"environment":{"arch":"x64","ecosystem":"cargo","executionContext":"rust","language":"rust","os":"linux","packageManager":"cargo","runtime":"rust","schemaVersion":1},"license":"MIT-0","packages":["pkg:cargo/tower-service@0.3.3"],"schemaVersion":1,"symbols":["tower_service::Service","tower_service::Service::poll_ready","tower_service::Service::call"],"verifierAdapter":"cargo@1"}
//! Tower Service trait contract verification.
pub use tower_service::Service;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll, Wake, Waker};
use tower_service::Service;
struct TestWaker;
impl Wake for TestWaker {
fn wake(self: Arc<Self>) {}
}
fn make_context(waker: &Waker) -> Context<'_> {
Context::from_waker(waker)
}
struct PipelineService {
ready_permits: usize,
in_flight: Arc<AtomicUsize>,
}
impl PipelineService {
fn new(in_flight: Arc<AtomicUsize>) -> Self {
Self {
ready_permits: 0,
in_flight,
}
}
}
struct PendingResponseFuture {
in_flight: Arc<AtomicUsize>,
value: usize,
poll_count: usize,
}
impl Future for PendingResponseFuture {
type Output = Result<usize, std::convert::Infallible>;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
self.poll_count += 1;
if self.poll_count < 2 {
// Stay pending on first poll to verify concurrency/pipelining
Poll::Pending
} else {
self.in_flight.fetch_sub(1, Ordering::SeqCst);
Poll::Ready(Ok(self.value))
}
}
}
impl Service<usize> for PipelineService {
type Response = usize;
type Error = std::convert::Infallible;
type Future = PendingResponseFuture;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.ready_permits += 1;
Poll::Ready(Ok(()))
}
fn call(&mut self, req: usize) -> Self::Future {
assert!(
self.ready_permits > 0,
"Service::call invoked without available poll_ready permit"
);
self.ready_permits -= 1;
self.in_flight.fetch_add(1, Ordering::SeqCst);
PendingResponseFuture {
in_flight: self.in_flight.clone(),
value: req * 2,
poll_count: 0,
}
}
}
fn generic_dispatch<S>(mut svc: S, req: usize, cx: &mut Context<'_>) -> S::Future
where
S: Service<usize>,
{
match svc.poll_ready(cx) {
Poll::Ready(Ok(())) => svc.call(req),
_ => panic!("Service was not ready"),
}
}
#[test]
fn test_ref_mut_service_pipelining_without_borrow_retention() {
let waker = Waker::from(Arc::new(TestWaker));
let mut cx = make_context(&waker);
let in_flight = Arc::new(AtomicUsize::new(0));
let mut svc = PipelineService::new(in_flight.clone());
// Dispatch request 1 using &mut svc via generic Service<usize> consumer.
// The returned future S::Future is NOT bound to the lifetime of &mut svc.
let mut fut1 = generic_dispatch(&mut svc, 10, &mut cx);
// fut1 is polled once, remaining Pending; in_flight is 1
assert_eq!(Pin::new(&mut fut1).poll(&mut cx), Poll::Pending);
assert_eq!(in_flight.load(Ordering::SeqCst), 1);
// We can immediately borrow &mut svc AGAIN to poll and dispatch request 2
// while fut1 is still pending!
let mut fut2 = generic_dispatch(&mut svc, 20, &mut cx);
assert_eq!(Pin::new(&mut fut2).poll(&mut cx), Poll::Pending);
// Both requests are concurrently in-flight on the single unbuffered service instance
assert_eq!(in_flight.load(Ordering::SeqCst), 2);
// Dispatch request 3 on the same service instance
let mut fut3 = generic_dispatch(&mut svc, 30, &mut cx);
assert_eq!(Pin::new(&mut fut3).poll(&mut cx), Poll::Pending);
assert_eq!(in_flight.load(Ordering::SeqCst), 3);
// Second poll finishes all futures
assert_eq!(Pin::new(&mut fut1).poll(&mut cx), Poll::Ready(Ok(20)));
assert_eq!(Pin::new(&mut fut2).poll(&mut cx), Poll::Ready(Ok(40)));
assert_eq!(Pin::new(&mut fut3).poll(&mut cx), Poll::Ready(Ok(60)));
assert_eq!(in_flight.load(Ordering::SeqCst), 0);
}
#[test]
fn test_box_dyn_service_blanket_impl_and_assoc_type_requirements() {
let waker = Waker::from(Arc::new(TestWaker));
let mut cx = make_context(&waker);
let in_flight = Arc::new(AtomicUsize::new(0));
// Dynamic dispatch trait object requires defining Response, Error, AND Future
let mut boxed: Box<
dyn Service<
usize,
Response = usize,
Error = std::convert::Infallible,
Future = PendingResponseFuture,
>,
> = Box::new(PipelineService::new(in_flight.clone()));
// Box<S> where S: ?Sized implements Service directly
assert_eq!(boxed.poll_ready(&mut cx), Poll::Ready(Ok(())));
let mut fut = boxed.call(50);
assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Pending);
assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Ready(Ok(100)));
}
#[test]
fn test_poll_ready_permits_are_single_use_per_call() {
let waker = Waker::from(Arc::new(TestWaker));
let mut cx = make_context(&waker);
let in_flight = Arc::new(AtomicUsize::new(0));
let mut svc = PipelineService::new(in_flight);
// Readying once grants exactly one permit
assert_eq!(svc.poll_ready(&mut cx), Poll::Ready(Ok(())));
let mut fut = svc.call(5);
let _ = Pin::new(&mut fut).poll(&mut cx);
// Calling again without poll_ready violates the contract and panics in our service
let panic_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = svc.call(5);
}));
assert!(
panic_result.is_err(),
"Calling Service::call without poll_ready permit must fail"
);
}