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]
|
[package]
|
||||||
name = "managed"
|
name = "managed"
|
||||||
description = "Placeholder"
|
version = "0.1.0"
|
||||||
version = "0.0.0"
|
|
||||||
authors = ["whitequark <whitequark@whitequark.org>"]
|
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"
|
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)]
|
#![no_std]
|
||||||
mod tests {
|
#![cfg_attr(feature = "use_alloc", feature(alloc))]
|
||||||
#[test]
|
#![cfg_attr(feature = "use_collections", feature(collections))]
|
||||||
fn it_works() {
|
|
||||||
}
|
#[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