3.7 KiB
Managed
managed is a library that provides a way to logically own objects, whether or not heap allocation is available.
Motivation
The managed library exists at the intersection of three concepts: heap-less environments, collections and generic code. Consider this struct representing a network interface:
pub struct Interface<'a, 'b: 'a,
DeviceT: Device,
ProtocolAddrsT: BorrowMut<[IpAddress]>,
SocketsT: BorrowMut<[Socket<'a, 'b>]>
> {
device: DeviceT,
hardware_addr: EthernetAddress,
protocol_addrs: ProtocolAddrsT,
sockets: SocketsT,
phantom: PhantomData<Socket<'a, 'b>>
}
There are three things the struct Interface
is parameterized over:
- an object implementing the trait
DeviceT
, which it owns; - a slice of
IPAddress
es, which it either owns or borrows mutably; - a slice of
Socket
s, which it either owns or borrows mutably, and which further either own or borrow some memory.
The motivaion for using BorrowMut
is that in environments with heap, the struct ought to
own a Vec
; on the other hand, without heap there is neither Vec
nor Box
, and it is only
possible to use a &mut
. Both of these implement BorrowMut.
Note that owning a BorrowMut
in this way does not hide the concrete type inside BorrowMut
;
if the slice is backed by a Vec
then the Vec
may still be resized by external code,
although not the implementation of Interface
.
In isolation, this struct is easy to use. However, when combined with another codebase, perhaps embedded in a scheduler, problems arise. The type parameters have to go somewhere! There are two choices:
- either the type parameters, whole lot of them, infect the scheduler and push ownership even higher in the call stack (self-mutably-borrowing structs are not usable in safe Rust, so the scheduler could not easily own the slices);
- or the interface is owned as a boxed trait object, excluding heap-less systems.
Clearly, both options are unsatisfying. Enter managed!
Installation
To use the managed library in your project, add the following to Cargo.toml
:
[dependencies]
managed = "0.1"
The default configuration assumes a hosted environment, for ease of evaluation. You probably want to disable default features and configure them one by one:
[dependencies]
managed = { version = "...", default-features = false, features = ["..."] }
Feature use_std
The use_std
feature enables use of Box
and Vec
through a dependency on the std
crate.
Feature use_alloc
The use_alloc
feature enables use of Box
through a dependency on the alloc
crate.
This only works on nightly rustc.
Feature use_collections
The use_collections
feature enables use of Vec
through a dependency on
the collections
crate. This only works on nightly rustc.
Usage
managed is an interoperability crate: it does not include complex functionality but rather defines an interface that may be used by many downstream crates. It includes two enums:
pub enum Managed<'a, T: 'a + ?Sized> {
Borrowed(&'a mut T),
#[cfg(/* Box available */)]
Owned(Box<T>),
}
pub enum ManagedSlice<'a, T: 'a> {
Borrow(&'a mut [T]),
#[cfg(/* Vec available */)]
Owned(Vec<T>)
}
The enums have the From
implementations from the corresponding types, and Deref
/DerefMut
implementations to the type T
, as well as other helper methods; see the full documentation
for details.
Of course, the enums can be always matched explicitly as well.
License
managed is distributed under the terms of 0-clause BSD license.
See LICENSE-0BSD for details.