我正在 Teensy 4.0 (
--target thumbv7em-none-eabihf
) 上使用 Rust,这意味着我必须使用 #![no_std]
.
在某些情况下,我想根据旋转开关的位置执行不同的操作。
下面是一个玩具示例,说明了一个问题,我想返回一系列实现特征的对象中的一个。
fn text_for(selector: i32) -> impl Fn()->Box<dyn Iterator<Item=char>> {
match selector {
1 => || {
let rval : Box<dyn Iterator<Item=char>> = Box::new("author".chars());
rval
},
_ => || {
let rval : Box::<dyn Iterator<Item=char>> = Box::new(b"baseline".iter().map(|&b| (b) as char));
rval
}
}
}
不幸的是
Box
在no_std环境中不可用。 我看到了对 alloc
板条箱的引用(是否可以将 Box 与 no_std 一起使用?),但是当我使用 extern crate alloc; use alloc::boxed::Box;
时,编译器会抱怨
error: no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait.
error: `#[alloc_error_handler]` function required, but not found.
note: Use `#![feature(default_alloc_error_handler)]` for a default error handler.
尝试使用
alloc-cortex-m
板条箱来使用 CortexMHeap
作为 lkolbly 建议的 #[global_allocator]
会导致以下错误
error[E0554]: `#![feature]` may not be used on the stable release channel
--> /home/thoth/.cargo/registry/src/github.com-1ecc6299db9ec823/linked_list_allocator-0.8.11/src/lib.rs:1:41
|
1 | #![cfg_attr(feature = "const_mut_refs", feature(const_mut_refs))]
| ^^^^^^^^^^^^^^^^^^^^^^^
如何使用稳定的 rust 在 no_std 环境中处理特征的
dyn
实例?
alloc-cortex-m
已弃用,请使用embedded-alloc
这是一个全局分配器的示例,改编自
alloc-cortex-m
,但没有使用 const_mut_refs
的 linked_list_allocator
功能(需要每晚 Rust)。
注意:代码未经测试。
Cargo.toml:
[dependencies]
linked_list_allocator = { version = "0.9", default-features = false, features = ["use_spin"] }
lib.rs:
#![no_std]
mod allocator;
extern crate alloc;
use alloc::boxed::Box;
#[global_allocator]
static ALLOCATOR: allocator::CortexMHeap = unsafe { allocator::CortexMHeap::empty() };
#[entry]
fn main() -> ! {
// Initialize the allocator BEFORE you use it
let start = cortex_m_rt::heap_start() as usize;
let size = 1024; // in bytes
unsafe { ALLOCATOR.init(start, size) }
// ...
}
// Your code using `Box` here.
分配器.rs:
use core::alloc::{GlobalAlloc, Layout};
use core::cell::RefCell;
use core::ptr::{self, NonNull};
use core::mem::MaybeUninit;
use cortex_m::interrupt::Mutex;
use linked_list_allocator::Heap;
pub struct CortexMHeap {
heap: Mutex<RefCell<MaybeUninit<Heap>>>,
}
impl CortexMHeap {
/// Crate a new UNINITIALIZED heap allocator
///
/// # Safety
///
/// You must initialize this heap using the
/// [`init`](struct.CortexMHeap.html#method.init) method before using the allocator.
pub const unsafe fn empty() -> CortexMHeap {
CortexMHeap {
heap: Mutex::new(RefCell::new(MaybeUninit::uninit())),
}
}
fn heap(&self, cs: &cortex_m::interrupt::CriticalSection) -> &mut Heap {
let heap = &mut *self.heap.borrow(cs).borrow_mut();
// SAFETY: `init()` initializes this, and it's guaranteed to be called by preconditions of `empty()`.
unsafe { &mut *heap.as_mut_ptr() }
}
/// Initializes the heap
///
/// This function must be called BEFORE you run any code that makes use of the
/// allocator.
///
/// `start_addr` is the address where the heap will be located.
///
/// `size` is the size of the heap in bytes.
///
/// Note that:
///
/// - The heap grows "upwards", towards larger addresses. Thus `end_addr` must
/// be larger than `start_addr`
///
/// - The size of the heap is `(end_addr as usize) - (start_addr as usize)`. The
/// allocator won't use the byte at `end_addr`.
///
/// # Safety
///
/// Obey these or Bad Stuff will happen.
///
/// - This function must be called exactly ONCE.
/// - `size > 0`
pub unsafe fn init(&self, start_addr: usize, size: usize) {
cortex_m::interrupt::free(|cs| {
let heap = &mut *self.heap.borrow(cs).borrow_mut();
*heap = MaybeUninit::new(Heap::empty());
(*heap.as_mut_ptr()).init(start_addr, size);
});
}
/// Returns an estimate of the amount of bytes in use.
pub fn used(&self) -> usize {
cortex_m::interrupt::free(|cs| self.heap(cs).used())
}
/// Returns an estimate of the amount of bytes available.
pub fn free(&self) -> usize {
cortex_m::interrupt::free(|cs| self.heap(cs).free())
}
}
unsafe impl GlobalAlloc for CortexMHeap {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
cortex_m::interrupt::free(|cs| {
self.heap(cs)
.allocate_first_fit(layout)
.ok()
.map_or(ptr::null_mut(), |allocation| allocation.as_ptr())
})
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
cortex_m::interrupt::free(|cs| {
self.heap(cs)
.deallocate(NonNull::new_unchecked(ptr), layout)
});
}
}
另一个答案中显示的一个选项是通过提供分配器来允许使用盒子。但是,如果您不需要在代码中的其他任何地方进行分配,则没有必要仅仅为了特征对象而引入它们 - 您还可以通过借用堆栈分配的值来创建特征对象。
这需要更改 API,以便闭包不会返回迭代器,而是将其传递给用户提供的使用它的闭包。消费闭包可以迭代它并提取有用的数据。例如:
fn text_for<F, R>(selector: i32) -> impl Fn(F) -> R
where
F: Fn(&mut dyn Iterator<Item = char>) -> R,
{
move |consume| {
let (mut it1, mut it2);
let it: &mut dyn Iterator<Item = char> = match selector {
1 => {
it1 = "author".chars();
&mut it1
}
_ => {
it2 = b"baseline".iter().map(|&b| b as char);
&mut it2
}
};
consume(it)
}
}
您可以像这样使用这个版本的text_for()
:
fn main() {
let with_text_iter = text_for(1);
assert_eq!(
// example consume function just collects chars into Vec
with_text_iter(|it| it.collect::<Vec<_>>()),
&['a', 'u', 't', 'h', 'o', 'r']
);
}