pub struct Memory(/* private fields */);
Expand description
A WebAssembly linear memory.
WebAssembly memories represent a contiguous array of bytes that have a size that is always a multiple of the WebAssembly page size, currently 64 kilobytes.
WebAssembly memory is used for global data (not to be confused with wasm
global
items), statics in C/C++/Rust, shadow stack memory, etc. Accessing
wasm memory is generally quite fast.
Memories, like other wasm items, are owned by a Store
.
§Memory
and Safety
Linear memory is a lynchpin of safety for WebAssembly. In Wasmtime there are
safe methods of interacting with a Memory
:
Note that all of these consider the entire store context as borrowed for the
duration of the call or the duration of the returned slice. This largely
means that while the function is running you’ll be unable to borrow anything
else from the store. This includes getting access to the T
on
Store<T>
, but it also means that you can’t recursively
call into WebAssembly for instance.
If you’d like to dip your toes into handling Memory
in a more raw
fashion (e.g. by using raw pointers or raw slices), then there’s a few
important points to consider when doing so:
-
Any recursive calls into WebAssembly can possibly modify any byte of the entire memory. This means that whenever wasm is called Rust can’t have any long-lived borrows live across the wasm function call. Slices like
&mut [u8]
will be violated because they’re not actually exclusive at that point, and slices like&[u8]
are also violated because their contents may be mutated. -
WebAssembly memories can grow, and growth may change the base pointer. This means that even holding a raw pointer to memory over a wasm function call is also incorrect. Anywhere in the function call the base address of memory may change. Note that growth can also be requested from the embedding API as well.
As a general rule of thumb it’s recommended to stick to the safe methods of
Memory
if you can. It’s not advised to use raw pointers or unsafe
operations because of how easy it is to accidentally get things wrong.
Some examples of safely interacting with memory are:
use wasmtime::{Memory, Store, MemoryAccessError};
// Memory can be read and written safely with the `Memory::read` and
// `Memory::write` methods.
// An error is returned if the copy did not succeed.
fn safe_examples(mem: Memory, store: &mut Store<()>) -> Result<(), MemoryAccessError> {
let offset = 5;
mem.write(&mut *store, offset, b"hello")?;
let mut buffer = [0u8; 5];
mem.read(&store, offset, &mut buffer)?;
assert_eq!(b"hello", &buffer);
// Note that while this is safe care must be taken because the indexing
// here may panic if the memory isn't large enough.
assert_eq!(&mem.data(&store)[offset..offset + 5], b"hello");
mem.data_mut(&mut *store)[offset..offset + 5].copy_from_slice(b"bye!!");
Ok(())
}
It’s worth also, however, covering some examples of incorrect,
unsafe usages of Memory
. Do not do these things!
use wasmtime::{Memory, Store};
// NOTE: All code in this function is not safe to execute and may cause
// segfaults/undefined behavior at runtime. Do not copy/paste these examples
// into production code!
unsafe fn unsafe_examples(mem: Memory, store: &mut Store<()>) -> Result<()> {
// First and foremost, any borrow can be invalidated at any time via the
// `Memory::grow` function. This can relocate memory which causes any
// previous pointer to be possibly invalid now.
let pointer: &u8 = &*mem.data_ptr(&store);
mem.grow(&mut *store, 1)?; // invalidates `pointer`!
// println!("{}", *pointer); // FATAL: use-after-free
// Note that the use-after-free also applies to slices, whether they're
// slices of bytes or strings.
let mem_slice = std::slice::from_raw_parts(
mem.data_ptr(&store),
mem.data_size(&store),
);
let slice: &[u8] = &mem_slice[0x100..0x102];
mem.grow(&mut *store, 1)?; // invalidates `slice`!
// println!("{:?}", slice); // FATAL: use-after-free
// The `Memory` type may be stored in other locations, so if you hand
// off access to the `Store` then those locations may also call
// `Memory::grow` or similar, so it's not enough to just audit code for
// calls to `Memory::grow`.
let pointer: &u8 = &*mem.data_ptr(&store);
some_other_function(store); // may invalidate `pointer` through use of `store`
// println!("{:?}", pointer); // FATAL: maybe a use-after-free
// An especially subtle aspect of accessing a wasm instance's memory is
// that you need to be extremely careful about aliasing. Anyone at any
// time can call `data_unchecked()` or `data_unchecked_mut()`, which
// means you can easily have aliasing mutable references:
let ref1: &u8 = &*mem.data_ptr(&store).add(0x100);
let ref2: &mut u8 = &mut *mem.data_ptr(&store).add(0x100);
// *ref2 = *ref1; // FATAL: violates Rust's aliasing rules
Ok(())
}
Overall there’s some general rules of thumb when unsafely working with
Memory
and getting raw pointers inside of it:
- If you never have a “long lived” pointer into memory, you’re likely in the clear. Care still needs to be taken in threaded scenarios or when/where data is read, but you’ll be shielded from many classes of issues.
- Long-lived pointers must always respect Rust’a aliasing rules. It’s ok for shared borrows to overlap with each other, but mutable borrows must overlap with nothing.
- Long-lived pointers are only valid if they’re not invalidated for their
lifetime. This means that
Store
isn’t used to reenter wasm or the memory itself is never grown or otherwise modified/aliased.
At this point it’s worth reiterating again that unsafely working with
Memory
is pretty tricky and not recommended! It’s highly recommended to
use the safe methods to interact with Memory
whenever possible.
§Memory
Safety and Threads
Currently the wasmtime
crate does not implement the wasm threads proposal,
but it is planned to do so. It may be interesting to readers to see how this
affects memory safety and what was previously just discussed as well.
Once threads are added into the mix, all of the above rules still apply. There’s an additional consideration that all reads and writes can happen concurrently, though. This effectively means that any borrow into wasm memory are virtually never safe to have.
Mutable pointers are fundamentally unsafe to have in a concurrent scenario
in the face of arbitrary wasm code. Only if you dynamically know for sure
that wasm won’t access a region would it be safe to construct a mutable
pointer. Additionally even shared pointers are largely unsafe because their
underlying contents may change, so unless UnsafeCell
in one form or
another is used everywhere there’s no safety.
One important point about concurrency is that while Memory::grow
can
happen concurrently it will never relocate the base pointer. Shared
memories must always have a maximum size and they will be preallocated such
that growth will never relocate the base pointer. The current size of the
memory may still change over time though.
Overall the general rule of thumb for shared memories is that you must
atomically read and write everything. Nothing can be borrowed and everything
must be eagerly copied out. This means that Memory::data
and
Memory::data_mut
won’t work in the future (they’ll probably return an
error) for shared memories when they’re implemented. When possible it’s
recommended to use Memory::read
and Memory::write
which will still
be provided.
Implementations§
§impl Memory
impl Memory
pub fn new(store: impl AsContextMut, ty: MemoryType) -> Result<Memory, Error>
pub fn new(store: impl AsContextMut, ty: MemoryType) -> Result<Memory, Error>
Creates a new WebAssembly memory given the configuration of ty
.
The store
argument will be the owner of the returned Memory
. All
WebAssembly memory is initialized to zero.
§Panics
This function will panic if the Store
has a
ResourceLimiterAsync
(see also:
Store::limiter_async
). When
using an async resource limiter, use [Memory::new_async
] instead.
§Examples
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let memory_ty = MemoryType::new(1, None);
let memory = Memory::new(&mut store, memory_ty)?;
let module = Module::new(&engine, "(module (memory (import \"\" \"\") 1))")?;
let instance = Instance::new(&mut store, &module, &[memory.into()])?;
// ...
pub fn ty(&self, store: impl AsContext) -> MemoryType
pub fn ty(&self, store: impl AsContext) -> MemoryType
Returns the underlying type of this memory.
§Panics
Panics if this memory doesn’t belong to store
.
§Examples
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, "(module (memory (export \"mem\") 1))")?;
let instance = Instance::new(&mut store, &module, &[])?;
let memory = instance.get_memory(&mut store, "mem").unwrap();
let ty = memory.ty(&store);
assert_eq!(ty.minimum(), 1);
pub fn read(
&self,
store: impl AsContext,
offset: usize,
buffer: &mut [u8],
) -> Result<(), MemoryAccessError>
pub fn read( &self, store: impl AsContext, offset: usize, buffer: &mut [u8], ) -> Result<(), MemoryAccessError>
Safely reads memory contents at the given offset into a buffer.
The entire buffer will be filled.
If offset + buffer.len()
exceed the current memory capacity, then the
buffer is left untouched and a MemoryAccessError
is returned.
§Panics
Panics if this memory doesn’t belong to store
.
pub fn write(
&self,
store: impl AsContextMut,
offset: usize,
buffer: &[u8],
) -> Result<(), MemoryAccessError>
pub fn write( &self, store: impl AsContextMut, offset: usize, buffer: &[u8], ) -> Result<(), MemoryAccessError>
Safely writes contents of a buffer to this memory at the given offset.
If the offset + buffer.len()
exceeds the current memory capacity, then
none of the buffer is written to memory and a MemoryAccessError
is
returned.
§Panics
Panics if this memory doesn’t belong to store
.
pub fn data<'a, T>(&self, store: impl Into<StoreContext<'a, T>>) -> &'a [u8] ⓘwhere
T: 'a,
pub fn data<'a, T>(&self, store: impl Into<StoreContext<'a, T>>) -> &'a [u8] ⓘwhere
T: 'a,
Returns this memory as a native Rust slice.
Note that this method will consider the entire store context provided as borrowed for the duration of the lifetime of the returned slice.
§Panics
Panics if this memory doesn’t belong to store
.
pub fn data_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> &'a mut [u8] ⓘwhere
T: 'a,
pub fn data_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> &'a mut [u8] ⓘwhere
T: 'a,
Returns this memory as a native Rust mutable slice.
Note that this method will consider the entire store context provided as borrowed for the duration of the lifetime of the returned slice.
§Panics
Panics if this memory doesn’t belong to store
.
pub fn data_and_store_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> (&'a mut [u8], &'a mut T)where
T: 'a,
pub fn data_and_store_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> (&'a mut [u8], &'a mut T)where
T: 'a,
Same as Memory::data_mut
, but also returns the T
from the
StoreContextMut
.
This method can be used when you want to simultaneously work with the
T
in the store as well as the memory behind this Memory
. Using
Memory::data_mut
would consider the entire store borrowed, whereas
this method allows the Rust compiler to see that the borrow of this
memory and the borrow of T
are disjoint.
§Panics
Panics if this memory doesn’t belong to store
.
pub fn size(&self, store: impl AsContext) -> u64
pub fn size(&self, store: impl AsContext) -> u64
Returns the size, in WebAssembly pages, of this wasm memory.
§Panics
Panics if this memory doesn’t belong to store
.
pub fn grow(&self, store: impl AsContextMut, delta: u64) -> Result<u64, Error>
pub fn grow(&self, store: impl AsContextMut, delta: u64) -> Result<u64, Error>
Grows this WebAssembly memory by delta
pages.
This will attempt to add delta
more pages of memory on to the end of
this Memory
instance. If successful this may relocate the memory and
cause Memory::data_ptr
to return a new value. Additionally any
unsafely constructed slices into this memory may no longer be valid.
On success returns the number of pages this memory previously had before the growth succeeded.
§Errors
Returns an error if memory could not be grown, for example if it exceeds
the maximum limits of this memory. A
ResourceLimiter
is another example of
preventing a memory to grow.
§Panics
Panics if this memory doesn’t belong to store
.
This function will panic if the Store
has a
ResourceLimiterAsync
(see also:
Store::limiter_async
. When using an
async resource limiter, use [Memory::grow_async
] instead.
§Examples
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, "(module (memory (export \"mem\") 1 2))")?;
let instance = Instance::new(&mut store, &module, &[])?;
let memory = instance.get_memory(&mut store, "mem").unwrap();
assert_eq!(memory.size(&store), 1);
assert_eq!(memory.grow(&mut store, 1)?, 1);
assert_eq!(memory.size(&store), 2);
assert!(memory.grow(&mut store, 1).is_err());
assert_eq!(memory.size(&store), 2);
assert_eq!(memory.grow(&mut store, 0)?, 2);
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Memory
impl RefUnwindSafe for Memory
impl Send for Memory
impl Sync for Memory
impl Unpin for Memory
impl UnwindSafe for Memory
Blanket Implementations§
§impl<T> AnySync for T
impl<T> AnySync for T
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> CheckedConversion for T
impl<T> CheckedConversion for T
source§impl<T> CheckedConversion for T
impl<T> CheckedConversion for T
source§impl<T> CloneToUninit for Twhere
T: Copy,
impl<T> CloneToUninit for Twhere
T: Copy,
source§unsafe fn clone_to_uninit(&self, dst: *mut T)
unsafe fn clone_to_uninit(&self, dst: *mut T)
clone_to_uninit
)source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
source§default unsafe fn clone_to_uninit(&self, dst: *mut T)
default unsafe fn clone_to_uninit(&self, dst: *mut T)
clone_to_uninit
)§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§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.§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.§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.§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.§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.§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.§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.§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.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
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 more§impl<Src, Dest> IntoTuple<Dest> for Srcwhere
Dest: FromTuple<Src>,
impl<Src, Dest> IntoTuple<Dest> for Srcwhere
Dest: FromTuple<Src>,
fn into_tuple(self) -> Dest
source§impl<T, Outer> IsWrappedBy<Outer> for T
impl<T, Outer> IsWrappedBy<Outer> for T
source§impl<T, Outer> IsWrappedBy<Outer> for T
impl<T, Outer> IsWrappedBy<Outer> for T
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§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 more§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 more§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
§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
§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.§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.§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.§impl<T> Pointable for T
impl<T> Pointable for T
source§impl<T> SaturatedConversion for T
impl<T> SaturatedConversion for T
source§fn saturated_from<T>(t: T) -> Selfwhere
Self: UniqueSaturatedFrom<T>,
fn saturated_from<T>(t: T) -> Selfwhere
Self: UniqueSaturatedFrom<T>,
source§fn saturated_into<T>(self) -> Twhere
Self: UniqueSaturatedInto<T>,
fn saturated_into<T>(self) -> Twhere
Self: UniqueSaturatedInto<T>,
T
. Read moresource§impl<T> SaturatedConversion for T
impl<T> SaturatedConversion for T
source§fn saturated_from<T>(t: T) -> Selfwhere
Self: UniqueSaturatedFrom<T>,
fn saturated_from<T>(t: T) -> Selfwhere
Self: UniqueSaturatedFrom<T>,
source§fn saturated_into<T>(self) -> Twhere
Self: UniqueSaturatedInto<T>,
fn saturated_into<T>(self) -> Twhere
Self: UniqueSaturatedInto<T>,
T
. Read more§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self
from the equivalent element of its
superset. Read more§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self
is actually part of its subset T
(and can be converted to it).§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset
but without any property checks. Always succeeds.§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self
to the equivalent element of its superset.§impl<T> Tap for T
impl<T> Tap for T
§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 more§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 more§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 more§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 more§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 more§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 more§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.§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.§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.§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.§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.§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.§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.§impl<T> TryConv for T
impl<T> TryConv for T
source§impl<T, U> TryIntoKey<U> for Twhere
U: TryFromKey<T>,
impl<T, U> TryIntoKey<U> for Twhere
U: TryFromKey<T>,
type Error = <U as TryFromKey<T>>::Error
fn try_into_key(self) -> Result<U, <U as TryFromKey<T>>::Error>
source§impl<S, T> UncheckedInto<T> for Swhere
T: UncheckedFrom<S>,
impl<S, T> UncheckedInto<T> for Swhere
T: UncheckedFrom<S>,
source§fn unchecked_into(self) -> T
fn unchecked_into(self) -> T
unchecked_from
.source§impl<S, T> UncheckedInto<T> for Swhere
T: UncheckedFrom<S>,
impl<S, T> UncheckedInto<T> for Swhere
T: UncheckedFrom<S>,
source§fn unchecked_into(self) -> T
fn unchecked_into(self) -> T
unchecked_from
.source§impl<T, S> UniqueSaturatedInto<T> for S
impl<T, S> UniqueSaturatedInto<T> for S
source§fn unique_saturated_into(self) -> T
fn unique_saturated_into(self) -> T
T
.source§impl<T, S> UniqueSaturatedInto<T> for S
impl<T, S> UniqueSaturatedInto<T> for S
source§fn unique_saturated_into(self) -> T
fn unique_saturated_into(self) -> T
T
.