Implement.
This commit is contained in:
parent
a816d0d4c2
commit
b1e24a9826
|
@ -0,0 +1,23 @@
|
|||
language: rust
|
||||
matrix:
|
||||
include:
|
||||
- rust: stable
|
||||
env: FEATURES='use_std'
|
||||
- rust: beta
|
||||
env: FEATURES='use_std'
|
||||
- rust: nightly
|
||||
env: FEATURES=''
|
||||
- rust: nightly
|
||||
env: FEATURES='use_alloc'
|
||||
- rust: nightly
|
||||
env: FEATURES='use_collections'
|
||||
- rust: nightly
|
||||
env: FEATURES='use_alloc use_collections'
|
||||
script:
|
||||
- cargo build --features "$FEATURES"
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#m-labs"
|
||||
use_notice: true
|
||||
skip_join: true
|
15
Cargo.toml
15
Cargo.toml
|
@ -1,8 +1,17 @@
|
|||
[package]
|
||||
name = "managed"
|
||||
description = "Placeholder"
|
||||
version = "0.0.0"
|
||||
version = "0.1.0"
|
||||
authors = ["whitequark <whitequark@whitequark.org>"]
|
||||
description = "An interface for logically owning objects, whether or not heap allocation is available."
|
||||
documentation = "https://docs.rs/managed/"
|
||||
homepage = "https://github.com/m-labs/rust-managed"
|
||||
repository = "https://github.com/m-labs/rust-managed.git"
|
||||
readme = "README.md"
|
||||
keywords = ["ownership"]
|
||||
license = "0BSD"
|
||||
|
||||
[dependencies]
|
||||
[features]
|
||||
use_std = []
|
||||
use_alloc = []
|
||||
use_collections = []
|
||||
default = ["use_std"]
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
Copyright (C) 2017 whitequark@whitequark.org
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
115
README.md
115
README.md
|
@ -1 +1,114 @@
|
|||
See https://lab.whitequark.org/notes/2016-12-17/owning-collections-in-heap-less-rust/
|
||||
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:
|
||||
|
||||
```rust
|
||||
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`:
|
||||
|
||||
```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:
|
||||
|
||||
```toml
|
||||
[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:
|
||||
|
||||
```rust
|
||||
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][doc]
|
||||
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](LICENSE-0BSD.txt) for details.
|
||||
|
|
22
src/lib.rs
22
src/lib.rs
|
@ -1,6 +1,16 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
}
|
||||
}
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "use_alloc", feature(alloc))]
|
||||
#![cfg_attr(feature = "use_collections", feature(collections))]
|
||||
|
||||
#[cfg(feature = "use_std")]
|
||||
extern crate std;
|
||||
#[cfg(feature = "use_alloc")]
|
||||
extern crate alloc;
|
||||
#[cfg(feature = "use_collections")]
|
||||
extern crate collections;
|
||||
|
||||
mod object;
|
||||
mod slice;
|
||||
|
||||
pub use object::Managed;
|
||||
pub use slice::ManagedSlice;
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
use core::ops::{Deref, DerefMut};
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "use_std")]
|
||||
use std::boxed::Box;
|
||||
#[cfg(feature = "use_alloc")]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "use_std")]
|
||||
use std::vec::Vec;
|
||||
#[cfg(all(feature = "use_alloc", feature = "use_collections"))]
|
||||
use collections::vec::Vec;
|
||||
|
||||
/// A managed object.
|
||||
///
|
||||
/// This enum can be used to represent exclusive access to objects. In Rust, exclusive access
|
||||
/// to an object is obtained by either owning the object, or owning a mutable pointer
|
||||
/// to the object; hence, "managed".
|
||||
///
|
||||
/// The purpose of this enum is providing good ergonomics with `std` present while making
|
||||
/// it possible to avoid having a heap at all (which of course means that `std` is not present).
|
||||
/// To achieve this, the variants other than `Borrow` are only available when the corresponding
|
||||
/// feature is opted in.
|
||||
///
|
||||
/// A function that requires a managed object should be generic over an `Into<Managed<'a, T>>`
|
||||
/// argument; then, it will be possible to pass either a `Box<T>`, `Vec<T>`, or a `&'a mut T`
|
||||
/// without any conversion at the call site.
|
||||
///
|
||||
/// Note that a `Vec<T>` converted into an `Into<Managed<'static, [T]>>` gets transformed
|
||||
/// into a boxed slice, and can no longer be resized. See also
|
||||
/// [ManagedSlice][struct.ManagedSlice.html], which does not have this drawback.
|
||||
pub enum Managed<'a, T: 'a + ?Sized> {
|
||||
/// Borrowed variant.
|
||||
Borrowed(&'a mut T),
|
||||
/// Owned variant, only available with the `use_std` or `use_alloc` feature enabled.
|
||||
#[cfg(any(feature = "use_std", feature = "use_alloc"))]
|
||||
Owned(Box<T>)
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + ?Sized> fmt::Debug for Managed<'a, T>
|
||||
where T: fmt::Debug {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Managed::Borrowed(ref x) => write!(f, "Borrowed({:?})", x),
|
||||
#[cfg(any(feature = "use_std", feature = "use_alloc"))]
|
||||
&Managed::Owned(ref x) => write!(f, "Owned({:?})", x)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + ?Sized> From<&'a mut T> for Managed<'a, T> {
|
||||
fn from(value: &'a mut T) -> Self {
|
||||
Managed::Borrowed(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "use_std", feature = "use_alloc"))]
|
||||
impl<T: ?Sized + 'static> From<Box<T>> for Managed<'static, T> {
|
||||
fn from(value: Box<T>) -> Self {
|
||||
Managed::Owned(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "use_std", all(feature = "use_alloc", feature = "use_collections")))]
|
||||
impl<T: 'static> From<Vec<T>> for Managed<'static, [T]> {
|
||||
fn from(value: Vec<T>) -> Self {
|
||||
Managed::Owned(value.into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + ?Sized> Deref for Managed<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
&Managed::Borrowed(ref value) => value,
|
||||
#[cfg(any(feature = "use_std", feature = "use_alloc"))]
|
||||
&Managed::Owned(ref value) => value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + ?Sized> DerefMut for Managed<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
&mut Managed::Borrowed(ref mut value) => value,
|
||||
#[cfg(any(feature = "use_std", feature = "use_alloc"))]
|
||||
&mut Managed::Owned(ref mut value) => value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
use core::ops::{Deref, DerefMut};
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "use_std")]
|
||||
use std::vec::Vec;
|
||||
#[cfg(feature = "use_collections")]
|
||||
use collections::vec::Vec;
|
||||
|
||||
/// A managed slice.
|
||||
///
|
||||
/// This enum can be used to represent exclusive access to slices of objects.
|
||||
/// In Rust, exclusive access to an object is obtained by either owning the object,
|
||||
/// or owning a mutable pointer to the object; hence, "managed".
|
||||
///
|
||||
/// The purpose of this enum is providing good ergonomics with `std` present while making
|
||||
/// it possible to avoid having a heap at all (which of course means that `std` is not present).
|
||||
/// To achieve this, the variants other than `Borrow` are only available when the corresponding
|
||||
/// feature is opted in.
|
||||
///
|
||||
/// A function that requires a managed object should be generic over an `Into<ManagedSlice<'a, T>>`
|
||||
/// argument; then, it will be possible to pass either a `Vec<T>`, or a `&'a mut [T]`
|
||||
/// without any conversion at the call site.
|
||||
///
|
||||
/// See also [Managed][struct.Managed.html].
|
||||
pub enum ManagedSlice<'a, T: 'a> {
|
||||
/// Borrowed variant.
|
||||
Borrowed(&'a mut [T]),
|
||||
/// Owned variant, only available with the `use_std` or `use_collections` feature enabled.
|
||||
#[cfg(any(feature = "use_std", feature = "use_collections"))]
|
||||
Owned(Vec<T>)
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> fmt::Debug for ManagedSlice<'a, T>
|
||||
where T: fmt::Debug {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&ManagedSlice::Borrowed(ref x) => write!(f, "Borrowed({:?})", x),
|
||||
#[cfg(any(feature = "use_std", feature = "use_collections"))]
|
||||
&ManagedSlice::Owned(ref x) => write!(f, "Owned({:?})", x)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> From<&'a mut [T]> for ManagedSlice<'a, T> {
|
||||
fn from(value: &'a mut [T]) -> Self {
|
||||
ManagedSlice::Borrowed(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "use_std", feature = "use_collections"))]
|
||||
impl<T: 'static> From<Vec<T>> for ManagedSlice<'static, T> {
|
||||
fn from(value: Vec<T>) -> Self {
|
||||
ManagedSlice::Owned(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> Deref for ManagedSlice<'a, T> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
&ManagedSlice::Borrowed(ref value) => value,
|
||||
#[cfg(any(feature = "use_std", feature = "use_collections"))]
|
||||
&ManagedSlice::Owned(ref value) => value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> DerefMut for ManagedSlice<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
&mut ManagedSlice::Borrowed(ref mut value) => value,
|
||||
#[cfg(any(feature = "use_std", feature = "use_collections"))]
|
||||
&mut ManagedSlice::Owned(ref mut value) => value
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue