pub struct SurfpoolWsRpc {
pub uid: AtomicUsize,
pub signature_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<Response<RpcSignatureResult>>>>>,
pub account_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<Response<UiAccount>>>>>,
pub program_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<Response<RpcKeyedAccount>>>>>,
pub slot_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<SlotInfo>>>>,
pub logs_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<Response<RpcLogsResponse>>>>>,
pub snapshot_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<SnapshotImportNotification>>>>,
pub tokio_handle: Handle,
}Expand description
WebSocket RPC server implementation for Surfpool.
This struct manages WebSocket subscriptions for both signature status updates and account change notifications in the Surfpool environment. It provides a complete WebSocket RPC interface that allows clients to subscribe to real-time updates from the Solana Virtual Machine (SVM) and handles the lifecycle of WebSocket connections.
§Fields
uid: Atomic counter for generating unique subscription IDs across all subscription types.signature_subscription_map: Thread-safe HashMap containing active signature subscriptions, mapping subscription IDs to their notification sinks.account_subscription_map: Thread-safe HashMap containing active account subscriptions, mapping subscription IDs to their notification sinks.slot_subscription_map: Thread-safe HashMap containing active slot subscriptions, mapping subscription IDs to their notification sinks.tokio_handle: Runtime handle for spawning asynchronous subscription monitoring tasks.
§Features
- Concurrent Subscriptions: Supports multiple simultaneous subscriptions without blocking.
- Thread Safety: All subscription management operations are thread-safe using RwLock.
- Automatic Cleanup: Subscriptions are automatically cleaned up when completed or unsubscribed.
- Efficient Monitoring: Each subscription runs in its own async task for optimal performance.
- Real-time Updates: Provides immediate notifications when monitored conditions are met.
§Usage
This struct implements the Rpc trait and is typically used as part of a larger
WebSocket server infrastructure to provide real-time blockchain data to clients.
§Notes
- Each subscription is assigned a unique numeric ID for tracking and management.
- The struct maintains separate maps for different subscription types to optimize performance.
- All async operations are managed through the provided Tokio runtime handle.
§See Also
Rpc: The trait interface this struct implementsRpcAccountSubscribeConfig: Configuration options for account subscriptions
Fields§
§uid: AtomicUsize§signature_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<Response<RpcSignatureResult>>>>>§account_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<Response<UiAccount>>>>>§program_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<Response<RpcKeyedAccount>>>>>§slot_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<SlotInfo>>>>§logs_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<Response<RpcLogsResponse>>>>>§snapshot_subscription_map: Arc<RwLock<HashMap<SubscriptionId, Sink<SnapshotImportNotification>>>>§tokio_handle: HandleTrait Implementations§
Source§impl Rpc for SurfpoolWsRpc
impl Rpc for SurfpoolWsRpc
Source§fn signature_subscribe(
&self,
meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<RpcSignatureResult>>,
signature_str: String,
config: Option<RpcSignatureSubscribeConfig>,
)
fn signature_subscribe( &self, meta: Self::Metadata, subscriber: Subscriber<RpcResponse<RpcSignatureResult>>, signature_str: String, config: Option<RpcSignatureSubscribeConfig>, )
Implementation of signature subscription for WebSocket clients.
This method handles the complete lifecycle of signature subscriptions:
- Validates the provided signature string format
- Determines the subscription type (received vs commitment-based)
- Checks if the transaction already exists in the desired state
- If found and confirmed, immediately notifies the subscriber
- Otherwise, sets up a continuous monitoring loop
- Spawns an async task to handle ongoing subscription management
§Error Handling
- Rejects subscription with
InvalidParamsfor malformed signatures - Handles RPC context retrieval failures
- Manages subscription cleanup on completion or failure
§Concurrency
Each subscription runs in its own async task, allowing multiple concurrent subscriptions without blocking each other.
Source§fn signature_unsubscribe(
&self,
_meta: Option<Self::Metadata>,
subscription: SubscriptionId,
) -> Result<bool>
fn signature_unsubscribe( &self, _meta: Option<Self::Metadata>, subscription: SubscriptionId, ) -> Result<bool>
Implementation of signature unsubscription for WebSocket clients.
This method removes an active signature subscription from the internal tracking maps, effectively stopping further notifications for that subscription.
§Implementation Details
- Attempts to remove the subscription from the active subscriptions map
- Returns success if the subscription existed and was removed
- Returns an error if the subscription ID was not found
§Thread Safety
Uses write locks to ensure thread-safe removal from the subscription map.
Source§fn account_subscribe(
&self,
meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<UiAccount>>,
pubkey_str: String,
config: Option<RpcAccountSubscribeConfig>,
)
fn account_subscribe( &self, meta: Self::Metadata, subscriber: Subscriber<RpcResponse<UiAccount>>, pubkey_str: String, config: Option<RpcAccountSubscribeConfig>, )
Implementation of account subscription for WebSocket clients.
This method handles the complete lifecycle of account subscriptions:
- Validates the provided public key string format
- Parses the subscription configuration (commitment and encoding)
- Generates a unique subscription ID and assigns it to the subscriber
- Spawns an async task to continuously monitor account changes
- Sends notifications whenever the account state changes
§Monitoring Loop
The spawned task runs a continuous loop that:
- Checks if the subscription is still active (not unsubscribed)
- Polls for account updates from the SVM
- Sends notifications to the subscriber when changes occur
- Automatically terminates when the subscription is removed
§Error Handling
- Rejects subscription with
InvalidParamsfor malformed public keys - Handles encoding configuration for account data serialization
- Manages subscription cleanup through the monitoring loop
§Performance
Uses efficient polling with minimal CPU overhead and automatic cleanup when subscriptions are no longer needed.
Source§fn account_unsubscribe(
&self,
_meta: Option<Self::Metadata>,
subscription: SubscriptionId,
) -> Result<bool>
fn account_unsubscribe( &self, _meta: Option<Self::Metadata>, subscription: SubscriptionId, ) -> Result<bool>
Implementation of account unsubscription for WebSocket clients.
This method removes an active account subscription from the internal tracking maps, effectively stopping further notifications for that subscription. The monitoring loop in the corresponding subscription task will detect this removal and automatically terminate.
§Implementation Details
- Attempts to remove the subscription from the account subscriptions map
- Returns success if the subscription existed and was removed
- Returns an error if the subscription ID was not found
- The removal triggers automatic cleanup of the monitoring task
§Thread Safety
Uses write locks to ensure thread-safe removal from the subscription map. The monitoring task uses read locks to check subscription status, creating a clean synchronization pattern.
Source§fn program_subscribe(
&self,
meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<RpcKeyedAccount>>,
pubkey_str: String,
config: Option<RpcProgramSubscribeConfig>,
)
fn program_subscribe( &self, meta: Self::Metadata, subscriber: Subscriber<RpcResponse<RpcKeyedAccount>>, pubkey_str: String, config: Option<RpcProgramSubscribeConfig>, )
Implementation of program subscription for WebSocket clients.
This method handles the complete lifecycle of program subscriptions:
- Validates the provided program public key string format
- Parses the subscription configuration (commitment, encoding, and filters)
- Generates a unique subscription ID and assigns it to the subscriber
- Spawns an async task to continuously monitor account changes for the program
- Sends notifications whenever an account owned by the program changes and matches filters
§Monitoring Loop
The spawned task runs a continuous loop that:
- Checks if the subscription is still active (not unsubscribed)
- Polls for program account updates from the SVM
- Applies configured filters (dataSize, memcmp) before notifying
- Sends
RpcKeyedAccountnotifications (including account pubkey) to the subscriber - Automatically terminates when the subscription is removed
§Error Handling
- Rejects subscription with
InvalidParamsfor malformed public keys - Handles encoding configuration for account data serialization
- Manages subscription cleanup through the monitoring loop
§Performance
Uses efficient polling with minimal CPU overhead and automatic cleanup when subscriptions are no longer needed.
Source§fn program_unsubscribe(
&self,
_meta: Option<Self::Metadata>,
subscription: SubscriptionId,
) -> Result<bool>
fn program_unsubscribe( &self, _meta: Option<Self::Metadata>, subscription: SubscriptionId, ) -> Result<bool>
Implementation of program unsubscription for WebSocket clients.
This method removes an active program subscription from the internal tracking maps, effectively stopping further notifications for that subscription. The monitoring loop in the corresponding subscription task will detect this removal and automatically terminate.
§Implementation Details
- Attempts to remove the subscription from the program subscriptions map
- Returns success if the subscription existed and was removed
- Returns an error if the lock could not be acquired
- The removal triggers automatic cleanup of the monitoring task
§Thread Safety
Uses write locks to ensure thread-safe removal from the subscription map. The monitoring task uses read locks to check subscription status, creating a clean synchronization pattern.
type Metadata = Option<SurfpoolWebsocketMeta>
Source§fn slot_subscribe(&self, meta: Self::Metadata, subscriber: Subscriber<SlotInfo>)
fn slot_subscribe(&self, meta: Self::Metadata, subscriber: Subscriber<SlotInfo>)
Source§fn slot_unsubscribe(
&self,
_meta: Option<Self::Metadata>,
subscription: SubscriptionId,
) -> Result<bool>
fn slot_unsubscribe( &self, _meta: Option<Self::Metadata>, subscription: SubscriptionId, ) -> Result<bool>
Source§fn logs_subscribe(
&self,
meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<RpcLogsResponse>>,
mentions: Option<RpcTransactionLogsFilter>,
commitment: Option<CommitmentConfig>,
)
fn logs_subscribe( &self, meta: Self::Metadata, subscriber: Subscriber<RpcResponse<RpcLogsResponse>>, mentions: Option<RpcTransactionLogsFilter>, commitment: Option<CommitmentConfig>, )
Source§fn logs_unsubscribe(
&self,
_meta: Option<Self::Metadata>,
subscription: SubscriptionId,
) -> Result<bool>
fn logs_unsubscribe( &self, _meta: Option<Self::Metadata>, subscription: SubscriptionId, ) -> Result<bool>
fn root_subscribe( &self, meta: Self::Metadata, _subscriber: Subscriber<RpcResponse<()>>, )
fn root_unsubscribe( &self, _meta: Option<Self::Metadata>, _subscription: SubscriptionId, ) -> Result<bool>
fn slots_updates_subscribe( &self, meta: Self::Metadata, _subscriber: Subscriber<RpcResponse<()>>, )
fn slots_updates_unsubscribe( &self, _meta: Option<Self::Metadata>, _subscription: SubscriptionId, ) -> Result<bool>
fn block_subscribe( &self, meta: Self::Metadata, _subscriber: Subscriber<RpcResponse<()>>, )
fn block_unsubscribe( &self, _meta: Option<Self::Metadata>, _subscription: SubscriptionId, ) -> Result<bool>
fn vote_subscribe( &self, meta: Self::Metadata, _subscriber: Subscriber<RpcResponse<()>>, )
fn vote_unsubscribe( &self, _meta: Option<Self::Metadata>, _subscription: SubscriptionId, ) -> Result<bool>
Source§fn snapshot_subscribe(
&self,
meta: Self::Metadata,
subscriber: Subscriber<SnapshotImportNotification>,
snapshot_url: String,
)
fn snapshot_subscribe( &self, meta: Self::Metadata, subscriber: Subscriber<SnapshotImportNotification>, snapshot_url: String, )
Source§fn snapshot_unsubscribe(
&self,
_meta: Option<Self::Metadata>,
subscription: SubscriptionId,
) -> Result<bool>
fn snapshot_unsubscribe( &self, _meta: Option<Self::Metadata>, subscription: SubscriptionId, ) -> Result<bool>
Source§fn to_delegate(self) -> IoDelegate<Self, Self::Metadata>
fn to_delegate(self) -> IoDelegate<Self, Self::Metadata>
IoDelegate, wiring rpc calls to the trait methods.Auto Trait Implementations§
impl !Freeze for SurfpoolWsRpc
impl RefUnwindSafe for SurfpoolWsRpc
impl Send for SurfpoolWsRpc
impl Sync for SurfpoolWsRpc
impl Unpin for SurfpoolWsRpc
impl UnsafeUnpin for SurfpoolWsRpc
impl UnwindSafe for SurfpoolWsRpc
Blanket Implementations§
Source§impl<T> AggregateExpressionMethods for T
impl<T> AggregateExpressionMethods for T
Source§fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
DISTINCT modifier for aggregate functions Read moreSource§fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
ALL modifier for aggregate functions Read moreSource§fn aggregate_filter<P>(self, f: P) -> Self::Output
fn aggregate_filter<P>(self, f: P) -> Self::Output
Source§fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoSql for T
impl<T> IntoSql for T
Source§fn into_sql<T>(self) -> Self::Expression
fn into_sql<T>(self) -> Self::Expression
self to an expression for Diesel’s query builder. Read moreSource§fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
&self to an expression for Diesel’s query builder. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.