Is there a way to implement trait objects completely in stack memory?
This is the code that I use Box and thus heap memory:
extern crate alloc;
use alloc::vec::Vec;
use alloc::boxed::Box;
pub trait ConnectionImp {
fn send_data(&self);
}
pub struct Collector {
pub connections: Vec<Box<dyn ConnectionImp>>
}
impl Collector {
pub fn new() -> Collector {
Collector {
connections: Vec::with_capacity(5),
}
}
pub fn add_connection(&mut self,conn: Box<dyn ConnectionImp> ){
self.connections.push(conn);
}
}
I tried to use heapless crate but I could not find any replacement for Box. The following code shows the result of my effort:
use heapless::{Vec,/*pool::Box*/};
extern crate alloc;
use alloc::boxed::Box;
pub trait ConnectionImp {
fn send_data(&self);
}
pub struct Collector {
pub connections: Vec<Box<dyn ConnectionImp>,5>
}
impl Collector {
pub fn new() -> Collector {
Collector {
connections: Vec::new(),
}
}
pub fn add_connection(&mut self, conn: Box<dyn ConnectionImp> ){
self.connections.push(conn);
}
}
Yes, you can use &dyn Trait. A lot of examples of dynamic dispatch use Box because it's a more common use-case and using references introduces lifetimes, which tend to make examples more complicated.
Your code would become:
pub struct Collector<'a> {
pub connections: Vec<&'a dyn ConnectionImp>,
}
impl<'a> Collector<'a> {
pub fn new() -> Collector<'a> {
Collector {
connections: Vec::new(),
}
}
pub fn add_connection(&mut self, conn: &'a dyn ConnectionImp) {
self.connections.push(conn);
}
}
Related
Let's say I have a mutable structure (i32 in the example code for simplicity) which should be shared between threads. I can use Mutex for it (and Arc for memory management). But I want to have RAII wrapper for data lock to make some specific interface of data modification, track unlocks etc.
Here the draft (I don't know which lifetime specify for MutexGuard):
use std::sync::{Arc, Mutex, MutexGuard};
pub struct SharedData {
data: Arc<Mutex<i32>>
}
impl SharedData {
pub fn new(value: i32) -> Self {
SharedData {
data: Arc::new(Mutex::new(value))
}
}
pub fn lock(&self) -> LockedSharedData<'_> {
LockedSharedData::new(self.data.clone())
}
}
pub struct LockedSharedData<'a> {
_data: Arc<Mutex<i32>>,
guard: MutexGuard<'a, i32>
}
impl<'a> LockedSharedData<'a> {
fn new(data: Arc<Mutex<i32>>) -> Self {
LockedSharedData {
guard: data.lock().unwrap(),
_data: data
}
}
pub fn get(&self) -> i32 {
*self.guard
}
pub fn inc(&mut self) {
*self.guard += 1;
}
}
It predictable doesn't compile - https://rust.godbolt.org/z/4rEe3fWxK.
How to fix this pattern implementation?
In the following example, is there any way to access the comp_field within the implementation of BaseTrait for MyStruct?
pub trait CompositionTrait {
//methods
}
pub trait BaseTrait {
fn get_comp_field(&self) -> Box<dyn CompositionTrait>;
}
pub struct MyStruct {
pub comp_field: Box<dyn CompositionTrait>
}
impl MyStruct {
pub fn new(config: Config) -> Result<Self, Error> {
// here based on config, comp_field is assigned with different structs, all of which implements CompositionTrait.
}
}
impl BaseTrait for MyStruct {
fn get_comp_field(&self) -> Box<dyn CompositionTrait> {
self.comp_field // Error
}
}
The following error is presented for the current implementation:
cannot move out of `self.comp_field` which is behind a shared reference
move occurs because `self.comp_field` has type `std::boxed::Box<dyn BaseTrait::CompositionTrait>`, which does not implement the `Copy` trait
The eventual need for such implementation would be accessing the methods of comp_field in the functions which are based on BaseTrait.
The error occurs because you're trying to move the box out of the struct, but the struct is not mutable (and even if it was you would need to put something else in place of the box you're removing). Instead you should return a reference to the CompositionTrait:
pub trait CompositionTrait {
//methods
}
pub trait BaseTrait {
fn get_comp_field(&self) -> &dyn CompositionTrait;
}
pub struct MyStruct {
pub comp_field: Box<dyn CompositionTrait>
}
impl BaseTrait for MyStruct {
fn get_comp_field(&self) -> &dyn CompositionTrait {
&*self.comp_field // Or self.comp_field.as_ref()
}
}
Playground
I want to implement a generic way to use redis as a cluster or as a single node. It will have some specific functions to redis::Client and specific functions to redis::cluster::ClusterClient.
pub struct Redis<T> {
client: T
}
pub trait RedisClient {
fn new() -> Self;
}
impl RedisClient for Redis<redis::Client> {
fn new() -> Self {
Redis {
client: redis::Client::open("some_url").unwrap()
}
}
}
impl RedisClient for Redis<redis::cluster::ClusterClient> {
fn new() -> Self {
Redis {
client: redis::cluster::ClusterClient::open(["some_url"].to_vec()).unwrap()
}
}
}
But most of the functions are shared. How do I implement for both types at the same time? I would like to use it like this:
impl Redis<some_Type to accomodate both> {
pub fn set(&self) {}
pub fn get(&self) {}
}
so that I do not have to rewrite identical functionality twice like so:
impl Redis<redis::Client> {
pub fn set(&self) {}
pub fn get(&self) {}
}
impl Redis<redis::cluster::ClusterClient> {
pub fn set(&self) {}
pub fn get(&self) {}
}
The following way should work if I understand your problem correctly. Give it a try.
impl<T> Redis<T> {
pub fn set(&self) {}
pub fn get(&self) {}
}
The Rust playground code is here.
I have a struct of Token which has lifetime 'tok, and a scanner has lifetime 'lexer. I'm using both of them in another struct Parser, then I had a problem:
pub struct Token<'tok> {
pub value: Cow<'tok, str>,
pub line: usize,
}
pub struct Scanner {
pub source: Vec<char>,
pub current: usize,
pub line: usize,
}
pub struct Parser<'lexer> {
pub curr: &'lexer Token<'lexer>,
pub prev: &'lexer Token<'lexer>,
scanner: &'lexer mut Scanner,
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&mut self) {
self.prev = self.curr;
self.curr = &self.scanner.next(); // cannot inference lifetime
}
}
I think the problem is Token has lifetime 'tok, and the borrow checker doesn't know the relation between 'tok and 'lexer so it can't inference proper lifetime.
However, I can avoid the problem by modifying it into the updated code:
pub struct Parser<'lexer> {
pub curr: Token<'lexer>,
pub prev: Token<'lexer>,
scanner: &'lexer mut Scanner,
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&mut self) {
let prev = std::mem::replace(&mut self.curr, self.scanner.next());
self.prev = prev;
}
}
And with Token produced by next() is static:
impl Scanner {
pub fn next(&mut self) -> Token<'static> {
Token {
value: Cow::from(""),
line: 0,
}
}
}
It does compile but I think it's not ideal because all tokens are cloned from scanner into parser(they are not references anymore) and living until end of Parser's life. So memory usage is doubled. Is there a more proper way to deal with this?
Actualy your code structure is not ideal to deal with the borrow checker here why:
Token struct should be owned, the struct itself do not require any allocations (as the token is owned some indrection are required to allow prev <-> next switching)
None of the Parser or Lexer should own the base data so it will be easy to bound proper lifetime
In our case Vec<Char> is not a friendly type to work with (we do not need to own the data and this will be harder to make the comipler understand lifetimes), instead we're going to use an &'str but you could reproduce the exact behaviours with an &[char])
Here is an example that compile just fine
pub struct Token<'source> {
pub value: Cow<'source, str>,
pub line: usize,
}
pub struct Scanner<'source> {
pub source: &'source str,
pub current: usize,
pub line: usize,
}
pub struct Parser<'source> {
pub curr: Option<Token<'source>>,
pub prev: Option<Token<'source>>,
scanner: Scanner<'source>,
}
impl <'source>Scanner<'source> {
pub fn next(&'source /* DONT Forget to bound 'source to `self` */ self) -> Token<'source> {
Token {
value: Cow::from(self.source), /* `self.source` is bound to `'source` so the compiler understand that the token lifetime is the same than the source's one */
line: 0,
}
}
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&'lexer mut self) {
self.prev = self.curr.take();
self.curr = Some(self.scanner.next());
}
}
#![feature(rustc_private)]
extern crate rustc;
use rustc::hir::intravisit as hir_visit;
use rustc::hir;
use std::marker::PhantomData;
// ----------------------------------------------------------------------------
// Why does this compile?
// ----------------------------------------------------------------------------
pub struct Map<'a> {
pub _m: PhantomData<&'a ()>,
}
pub struct SomeVisitor<'a, 'tcx: 'a> {
pub map: &'a Map<'tcx>,
}
pub enum NestedVisitorMap<'this, 'tcx: 'this> {
None,
OnlyBodies(&'this Map<'tcx>),
All(&'this Map<'tcx>),
}
pub trait Visitor<'v>: Sized {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v>;
}
impl<'v, 'tcx> Visitor<'v> for SomeVisitor<'v, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
NestedVisitorMap::All(self.map)
}
}
// ----------------------------------------------------------------------------
// Why does this *not* compile?
// ----------------------------------------------------------------------------
pub struct SomeVisitor2<'a, 'tcx: 'a> {
pub map: &'a hir::map::Map<'tcx>,
}
impl<'v, 'tcx> hir_visit::Visitor<'v> for SomeVisitor2<'v, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, 'v> {
hir_visit::NestedVisitorMap::All(self.map)
}
}
fn main() {}
playground
NestedVisitorMap and Visitor
I recently ran into a lifetime issue and I decided to recreate it without any dependencies. The odd thing is that I can not recreate the lifetime error. To me both implementations look the same from the outside, but only one compiles successfully. Why is that?
rustc 1.21.0-nightly (e26688824 2017-08-27)
Update:
The problem seems to be the RefCell inside Map.
#[derive(Clone)]
pub struct Map<'hir> {
inlined_bodies: RefCell<rustc::util::nodemap::DefIdMap<&'hir hir::Body>>,
}
If there is a RefCell with a inner lifetime, it will trigger an error.
playground
Update2:
It turns out that I just mixed up lifetime subtyping. playground
I still don't know why only RefCell causes the error.