CodeSampleX

Beispiel

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

Verifiziertes Beispiel für cargo tower-service 0.3.3: Allow generic Service dispatch and request pipelining via &mut S blanket impl without tying the…

sha256:78b5761fe34700285b0b8908344c8a23d8055a321ae72d6d0c76f55e6d77e78a

Dieses Netzwerk bietet eine Sache: ein Sample, das baut. Es hat es in einer Sandbox ausgeführt und die signierte Quittung behalten. Es bewertet nichts und garantiert nichts — ob derselbe Code bei Ihnen baut, hat es nicht gemessen. Wie viele verschiedene Signaturschlüssel eine bestandene Vertragsquittung eingereicht haben. Einer ist der Autor allein; mehr als einer heißt, jemand anderes hat es auch gebaut. Ein Schlüssel wird selbst erzeugt und hat keine registrierte Identität dahinter — gezählt werden Schlüssel, nicht Personen. MIT-0

Ausführungsbelege

Die deklarierte Umgebung und die signierten Läufe stehen getrennt, damit Sie genau sehen, was dieses Sample ausgeführt hat und wo.

Beleggrundlage
Signierter Vertrag bestanden
Verifizierungsbelege
2
Signaturschlüssel, die es gebaut haben
2
Deklarierte Umgebung rust linux x64 rust rust cargo

Umgebungen der Verifizierungsläufe

Umgebung Contract Stufen Lauf
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

Fall

HOW
Ziel
Allow generic Service dispatch and request pipelining via &mut S blanket impl without tying the returned Future to the reference lifetime
Pakete
Symbole
  • tower_service::Service
  • tower_service::Service::poll_ready
  • tower_service::Service::call
Umgebung
rust
Erstellt
2026-08-16T11:21:18Z

Contract

  1. 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
  2. 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
  3. Each poll_ready readiness permit is single-use and consumed by the subsequent call, delegating transparently through &mut S and Box<S> blanket implementations

Dateien

  • Cargo.lock
  • Cargo.toml
  • NOTES.md
  • csx.json
  • src/lib.rs
  • tests/contract.rs

Quellartefakt herunterladen (tar.gz)

Quelltext

Cargo.lock
# 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"
Cargo.toml
[package]
name = "cargo-tower-service-api"
version = "0.1.0"
edition = "2021"

[dependencies]
tower-service = "0.3.3"
NOTES.md
# 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.
csx.json
{"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"}
src/lib.rs
//! Tower Service trait contract verification.
pub use tower_service::Service;
tests/contract.rs
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"
    );
}

Ursprungs-Seeder

csx-seed