Sharing resources in no-std environments
Work in Progress
This article describes ways to share some resources across multiple tasks.
fn main() {
println!("hello")
}
// hides interior mutability implementation detail (creation of the inner state), infects code with
// references
// Can't be copy
// PROs
// - hides interior mutability primitive (is this desired though? - embassy-mutex flexibility)
// CONs
// - can't have &mut self receiver
mod reference_outside {
use std::cell::RefCell;
struct Inner {
a: i32,
}
struct Outer(RefCell<Inner>);
impl Outer {
fn new() -> Self {
Self(RefCell::new(Inner { a: 0 }))
}
fn describe(&self) {
println!("a: {}", self.0.borrow().a)
}
// Can't pass &mut
fn modify(&self, a: i32) {
self.0.borrow_mut().a = a;
}
}
fn main() {
let outer = Outer::new();
}
fn a(outer: &Outer) {
outer.describe();
}
fn b(outer: &Outer) {
outer.modify(1);
}
}
// shows interior mutability implementation detail (creation of the inner state), infects code with
// lifetimes
// Is meant to be copied
// PROs
// - can have &mut self receiver - API shows intent better
// CONs
// - internal implementation detail is shown
mod reference_inside {
use std::cell::RefCell;
struct Inner {
a: i32,
}
#[derive(Clone, Copy)]
struct Outer<'a>(&'a RefCell<Inner>);
impl<'a> Outer<'a> {
fn new(inner: &'a RefCell<Inner>) -> Self {
Self(inner)
}
fn describe(&self) {
println!("a: {}", self.0.borrow().a)
}
fn modify(&mut self, a: i32) {
self.0.borrow_mut().a = a;
}
}
fn a(outer: Outer) {
outer.describe();
}
fn b(mut outer: Outer) {
outer.modify(1);
}
fn main() {
let inner = RefCell::new(Inner { a: 0 });
let outer = Outer::new(&inner);
a(outer);
b(outer);
}
}
// Hides interior mutability implementation detail (creation of the inner state), infects code with
// lifetimes
//
// Is meant to be copied
//
// Makes need to allocate resources still visible
//
// PROs
// - can have &mut self receiver - API shows intent better
// - internal implementation detail is hidden
// - handling of init with multiple resources is easier
// - still shows that there is some shared state
// CONs
// - Boilerplate, that should be removable with a macro
mod reference_inside_hide_state {
use std::{cell::RefCell, mem::MaybeUninit};
struct Inner {
a: i32,
}
struct OuterAllocations {
inner: MaybeUninit<RefCell<Inner>>,
}
impl Default for OuterAllocations {
fn default() -> Self {
OuterAllocations {
inner: MaybeUninit::uninit(),
}
}
}
#[derive(Clone, Copy)]
struct Outer<'a> {
inner: &'a RefCell<Inner>,
}
impl<'a> Outer<'a> {
// &'a mut here makes sure that allocations is not used multiple times
fn new(allocations: &'a mut OuterAllocations) -> Self {
let inner = &*allocations.inner.write(RefCell::new(Inner { a: 0 }));
Self { inner }
}
fn describe(&self) {
println!("a: {}", self.inner.borrow().a)
}
fn modify(&mut self, a: i32) {
self.inner.borrow_mut().a = a;
}
}
fn a(outer: Outer) {
outer.describe();
}
fn b(mut outer: Outer) {
outer.modify(1);
}
fn main() {
let mut allocations = OuterAllocations::default();
let outer = Outer::new(&mut allocations);
a(outer);
b(outer);
}
}