I don't really get what the issue is or even what you are trying to achieve. The definitions of ChildImporter and AnotherChildImporter and their "not working" new() functions are missing, and it's not clear what they are supposed to do, or in what way the current setup is not working.
Then you still need to get rid of the Box in Child. If you can do that, a reference to the logger might be enough: if not (for ownership reasons), and you cannot change the Box at Parent to Rc, then you may be able to hold a Rc to the parent in the child and let the parent implement log, as you suggested.
Not sure what exactly you are asking there; are you asking how you can get a &DataImporter from an Rc<DataImporter>? That's simple, Rc implements Deref so you can just access the methods and fields of the wrapped type directly, and an implicit coercion from &Rc<T> to &T will happen. You can also be explicit if you want, and obtain an intermediate reference via .as_ref() or .borrow().
I want to expose Logging to children. For example, let importer = AnotherChildImporter::new(self); // mismatched types
in an instance method of DataImporter.
I'm currently blocked by converting &self to Rc<dyn Logging>..
use std::{rc::Rc};
fn main() {
println!("Hello, world!");
}
pub enum OriginType {
Foo,
Bar,
}
pub struct ResultRow {}
#[derive(Debug)]
pub struct DataImporter {
logger: Box<dyn Logging>, // the `Box` here cannot be changed due to `uniffi-rs` (exposing this class for foreign languages
}
impl DataImporter {
fn import(&self, origin_type: OriginType, data: String) {
match origin_type {
OriginType::Foo => {
let importer = ChildImporter::new(self.to_owned()); // mismatched types
// expected struct `Rc<(dyn Logging + 'static)>`
// found reference `&DataImporter`
}
OriginType::Bar => {
let importer = AnotherChildImporter::new(self); // mismatched types
// expected struct `Rc<(dyn Logging + 'static)>`
// found reference `&DataImporter
// importer.import(origin_type, data)
}
}
}
}
// Child Importer would like to access the `Logging` functionality
pub struct ChildImporter {
logger: Rc<dyn Logging>,
}
impl ChildImporter {
fn new(logger: Rc<dyn Logging>) -> Self {
Self { logger: logger }
}
fn some_logic(&self) {
self.logger.log(LogLevel::Debug, "test".to_string());
}
}
pub struct AnotherChildImporter {
logger: Rc<dyn Logging>, // TODO: not sure the type here? e.g. Weak<dyn Logging>
}
impl AnotherChildImporter {
fn new(logger: Rc<dyn Logging>) -> Self {
Self { logger: logger }
}
}
pub enum LogLevel {
Debug,
Info,
Warn,
Error,
}
pub trait Logging: Send + Sync + std::fmt::Debug {
fn log(&self, level: LogLevel, message: String);
}
// Want to delegate the call to `self.logger` by `self`
impl Logging for DataImporter {
fn log(&self, level: LogLevel, message: String) {
self.logger.log(level, message);
}
}
You have to put the parent in an Rc from the very beginning, and construct its children by handing out copies of that Rc to them. Maybe you could try to use the custom self type Rc<Self>:
It's the same. This is nothing specific to Rc, this is a fundamental problem. If you only have a reference to a wrapped thing, you can't reliably (or safely, for that matter) get a reference to whatever it is wrapped in, because a reference (by means of its very type) doesn't convey any other information as to what the thing might be wrapped in.
This is simply a consequene of the compositionality and the locality of the type system. If you have a &Foo, it might have come from:
a temporary or a const, wherever the compiler pleases to put it in memory
a stack-allocated variable
a static
a heap-allocated buffer (Box, Rc, or anything else)
a thread-local variable
an FFI-allocated chunk of memory
and that reference shouldn't need to care whether it came from the stack or the heap or anywhere else, or whether the value is stored inside another struct or enum variant or tuple or array (or …). It would simply be too complicated having to deal with such non-local information. Types (and values) should be useful and interpretable on their own, without having to know all of the (open-ended and vast) context that their inhabiting values can possibly come from.