Need to implement trait for Rc<_>?

I'm working on a CLI utility which displays an indicatif::ProgressBar.
I made it possible to pass a custom progress bar by passing a lambda (pb_init) to .with_progress_init, so that in tests I can validate the output.

In a function setting up tests, I define a InMemoryTerm that will be used when drawing the progress bar. I need to define it outside of the pb_init, because I need to return it so I can use it in test assertions.

This is the excerpt of the test code causing trouble (complete code on github):

async fn setup_mocks -> ....... {
// other code
// ....
    let in_mem = Rc::new(InMemoryTerm::new(10, 80));
    let term = in_mem.clone();
    let pb_init = move |size| {
        ProgressBar::with_draw_target(
                Some(size),
                ProgressDrawTarget::term_like(Box::new(*term)))  
//  ----------------------------------------------------^^^^
// 1. cannot move out of an `Rc`
//   move occurs because value has type `indicatif::InMemoryTerm`,
//   which does not implement the `Copy` trait [E0507] 
    };

     let downloader = asfald::Downloader::new()
        .with_client(github_client)
        .with_progress_init(pb_init);

// other code
// ....
    
// fn returns this struct 
    GithubMock {
        cleanup: Box::new(cleanup),
        server_url,
        downloader,
        url,
        expected,
        pb_term: in_mem.clone(),
    }
}

If I dereference it like in the code above, I get the error in comment. If I don't dereference it, I get the error 1. the trait bound std::rc::Rcindicatif::InMemoryTerm: indicatif::TermLike is not satisfied. Does that mean I need to define a struct wrapping the Rc and implement the trait myself for that struct to delegate all calls to trait functions to the Rc value (which is automatically dereferenced)? Is there a trick to make it easy, just as an Rc is automatically dereferenced thanks to the Deref trait?

FYI I'm a recent rust dev, and might have misunderstood something :wink:

You have run afoul of the orphan rule.
Loosely stated, it means that whenever you need to implement a trait X for a type T, at least X or T needs to be defined in your own crate.

Wrapping the Rc value and then implementing the trait you mention for the wrapper type may or may not work here, I'm not sure what's involved in implementing that trait.

2 Likes

I'm still wondering if there's a better way, but seems that defining a struct which implements the trait allows it to be passed to term_like (though I also needed to swith to Arc from Rc to implement the trait) :

    let in_mem = Arc::new(InMemoryTerm::new(10, 80));
    let term = in_mem.clone();
    let pb_init = move |size| {
        let rc_term = RcTerm {
            inner: term.clone(),
        };
        ProgressBar::with_draw_target(Some(size), ProgressDrawTarget::term_like(Box::new(rc_term)))
    };

The struct and its trait implementation is this:

#[derive(Debug)]
struct RcTerm {
    inner: Arc<indicatif::InMemoryTerm>,
}

impl indicatif::TermLike for RcTerm {
    fn clear_line(&self) -> std::io::Result<()> {
        self.inner.clear_line()
    }
    fn width(&self) -> u16 {
        self.inner.width()
    }

    fn move_cursor_up(&self, n: usize) -> std::io::Result<()> {
        self.inner.move_cursor_up(n)
    }

    fn move_cursor_down(&self, n: usize) -> std::io::Result<()> {
        self.inner.move_cursor_down(n)
    }

    fn move_cursor_right(&self, n: usize) -> std::io::Result<()> {
        self.inner.move_cursor_right(n)
    }

    fn move_cursor_left(&self, n: usize) -> std::io::Result<()> {
        self.inner.move_cursor_left(n)
    }

    fn write_line(&self, s: &str) -> std::io::Result<()> {
        self.inner.write_line(s)
    }

    fn write_str(&self, s: &str) -> std::io::Result<()> {
        self.inner.write_str(s)
    }

    fn flush(&self) -> std::io::Result<()> {
        self.inner.flush()
    }
}
``

You could submit a PR/request for indicatif to add some implementations.

impl<T: ?Sized + TermLike> Termlike for Rc<T> { ... }
impl<T: ?Sized + TermLike> Termlike for Arc<T> { ... }
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.