Trait object type mismatch

Hi all, I am trying to build a Option of a boxed trait object based on an enum, however I can't manage to get rid of the move error or the only can get a reference of the trait object:

let emitter_dest = match &config.diagnostic_output {
            DiagnosticOutput::Default => None,
            DiagnosticOutput::Raw(dest) => Some(*dest.clone()),
};

The diagnosticoutput is a type in rustc, when the enum is Raw variant it contains a Box<dyn Write + Send>, I will need to cast it to None (Default), or Some(Box<dyn Write + Send>), how can I do that? The code I posted get this error:

DiagnosticOutput::Raw(dest) => Some(*dest.clone()),
   |                                                 ^^^^^^^^^^^^^ move occurs because value has type `std::boxed::Box<dyn std::io::Write + std::marker::Send>`, which does not implement the `Copy` trait

If I do:

let emitter_dest = match &config.diagnostic_output {
            DiagnosticOutput::Default => None,
            DiagnosticOutput::Raw(dest) => Some(dest.clone()),
};

I got this error:

 emitter_dest,
   |             ^^^^^^^^^^^^ expected struct `std::boxed::Box`, found reference
   |
   = note: expected enum `Option<std::boxed::Box<(dyn std::io::Write + std::marker::Send + 'static)>>`
              found enum `Option<&std::boxed::Box<dyn std::io::Write + std::marker::Send>>`

Maybe you should also give us more information about how/where emitter_dest is used. If that's a struct or a function expecting Option<Box<dyn Write + Send>>, so in particular an owned value, this means that you would need to move that Box out of the config.diagnostic field. Since that does (as you describe) already contain a trait object, you cannot just clone it. Your call to clone is basically doing nothing here, just copying a shared/immutable reference (“&T”). Now do you want to consume config here? What's the context?

A more complete presentation of the relevant code would help us help you. In case you do want to preserve config (not take ownership over its diagnostic_output field), then its just impossible to get an owned Option<Box<dyn Write + Send>> out of it that's passed to wherever emitter_dest is passed to and you need to change some struct definitions or function signatures involved.

Yes I do want to preserve the config, moreover, the context of my code example is the config method overwrite of compiler callback:

impl rustc_driver::Callbacks for ProjectCompilerCallbacks {
fn config(&mut self, config: &mut Config) {
        // println!("config error format: {:?} \n", config.opts.error_format);
        let mut emitter_dest = match &config.diagnostic_output {
            DiagnosticOutput::Default => None,
            DiagnosticOutput::Raw(dest) => Some(*dest),
        };

        
        config.parse_sess_created = Some(emitter::replace_emitter(
            config.opts.clone(),
            config.registry.clone(),
            dest,
        ));
    }
}

Where I only have a reference to the config, but I need to use the Option<Box<dyn Write + Send>> to create my own emitter just like this function:

fn default_emitter(
    sopts: &Options, 
    registry: Registry, 
    source_map: Lrc<SourceMap>, 
    emitter_dest: Option<Box<dyn Write + Send>>
) -> Box<dyn Emitter + Send>

Is there a way to move the box out of the diagnostic_output field given that the field doesn't impl Clone too? I am not sure that field is going to be used directly anyway so maybe I can move that data out and create emitter with it.

I see. I'm not familiar with these compiler libraries myself, but judging by the types involved, it might be a viable option to duplicate the dyn Write trait object by creating two handles that will merge into the original writer. Maybe using an Arc<Mutex<Box<dyn Write + Send>> with a wrapper that implements Write? Maybe add LineWriter to possibly reduce locking overhead and to avoid too much of a mess if two things want to write at the same time?

1 Like

If you want to "steal" the contents of the field, that's possible. E. g. using mem::replace. You must leave something in place of the thing you took, in this case probably the ::Default variant of the enum? It does modify the config though. I don't know what effect that could/would have in your setting.

1 Like

Nice, I will try both advice that you suggest, thank you so much!!

In case you try it, this approach involves mem::replace as well in order to gain ownership of the Box<dyn Write + Send>, just that afterwards, you put back one of the two writers you’ve created in order to keep the config “intact”.

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.