[Solved] Pass on a pointer to self as a specific trait pointer

Maybe we can discuss this on a bigger project before identifying the more general problem?

I do have a struct (called TabulatedBssrdf) which implements two traits:

  1. Bssrdf which in C++ would be the base class
  2. SeparableBssrdf which is already more specific (in C++ derived from Bssrdf), but basically just adds some more trait functions.

The C++ version uses another class (which in Rust is a struct implementing another trait) to basically store a pointer, which is used to call functions:

class SeparableBSSRDFAdapter : public BxDF {                                                                                                
  public:                                                                                                                                   
...
    Spectrum f(const Vector3f &wo, const Vector3f &wi) const {                                                                              
...
    }                                                                                                                                       
...

  private:                                                                                                                                  
    const SeparableBSSRDF *bssrdf;                                                                                                          
};                                                                                                                                          

The pointer is used here:

    Spectrum f(const Vector3f &wo, const Vector3f &wi) const {                                                                              
        Spectrum f = bssrdf->Sw(wi);                                                                                                        
        // Update BSSRDF transmission term to account for adjoint light                                                                     
        // transport                                                                                                                        
        if (bssrdf->mode == TransportMode::Radiance)                                                                                        
            f *= bssrdf->eta * bssrdf->eta;                                                                                                 
        return f;                                                                                                                           
    }                                                                                                                                       

The Rust counterpart would have to turn a &self reference (to a struct TabulatedBssrdf) into some kind of pointer to the SeparableBssrdf trait, so it's SeparableBssrdf::sw() function can be called:

impl Bssrdf for TabulatedBssrdf {                                                                                                           
...
    fn sample_s(                                                                                                                            
        &self,                                                                                                                              
...
    ) -> Spectrum {
...
            // initialize material model at sampled surface interaction                                                                     
            let mut bxdfs: Vec<Arc<Bxdf + Send + Sync>> = Vec::new();                                                                       
            bxdfs.push(Arc::new(SeparableBssrdfAdapter::new( // pointer needed here !!!                                                     
            )));                                                                                                                            
            si.bsdf = Some(Arc::new(Bsdf::new(&si, 1.0, bxdfs)));                                                                           
...
    }                                                                                                                                       
}                                                                                                                                           

Here is what needs to be done (commented out for now):

pub struct SeparableBssrdfAdapter {                                                                                                         
    // TODO: pub bssrdf: "some pointer to a" SeparableBssrdf;                                                                               
}                                                                                                                                           
                                                                                                                                            
impl SeparableBssrdfAdapter {                                                                                                               
    pub fn new(                                                                                                                             
        // TODO: bssrdf pointer parameter                                                                                                   
    ) -> Self {                                                                                                                             
        SeparableBssrdfAdapter {                                                                                                            
            // TODO: store pointer and maybe eta?                                                                                           
        }                                                                                                                                   
    }
}                                                                                                                                           
                                                                                                                                            
impl Bxdf for SeparableBssrdfAdapter {                                                                                                      
    fn f(&self, wo: &Vector3f, wi: &Vector3f) -> Spectrum {                                                                                 
        // let mut f: Spectrum = self.bssrdf.sw(wi);                                                                                        
                                                                                                                                            
        // update BSSRDF transmission term to account for adjoint light transport                                                           
                                                                                                                                            
        // if (self.bssrdf.mode == TransportMode::Radiance) {                                                                               
        //     f *= self.bssrdf.eta * self.bssrdf.eta;                                                                                      
        // }                                                                                                                                
        // f                                                                                                                                
        // WORK                                                                                                                             
        Spectrum::default()                                                                                                                 
    }                                                                                                                                       
    fn get_type(&self) -> u8 {                                                                                                              
        BxdfType::BsdfDiffuse as u8 | BxdfType::BsdfReflection as u8                                                                        
    }                                                                                                                                       
}

Maybe this is really easy and I just can't wrap my head around it? Any suggestions how to do this?

Take a look at Trait objects.

1 Like

Take a look at Trait objects.

@jethrogb Good idea, but then I run into another problem:

 error[E0277]: `dyn core::bssrdf::SeparableBssrdf` cannot be shared between threads safely                                                   
   --> src/core/bssrdf.rs:132:24                                                                                                            
    |                                                                                                                                       
132 |               bxdfs.push(Arc::new(SeparableBssrdfAdapter::new(                                                                        
    |  ________________________^                                                                                                            
133 | |                 self as &SeparableBssrdf,                                                                                           
134 | |             )));                                                                                                                    
    | |______________^ `dyn core::bssrdf::SeparableBssrdf` cannot be shared between threads safely                                          
    |                                                                                                                                       
    = help: the trait `std::marker::Sync` is not implemented for `dyn core::bssrdf::SeparableBssrdf`                                        
    = note: required because of the requirements on the impl of `std::marker::Send` for `&dyn core::bssrdf::SeparableBssrdf`                
    = note: required because it appears within the type `core::bssrdf::SeparableBssrdfAdapter<'_>`                                          
    = note: required for the cast to the object type `dyn core::reflection::Bxdf + std::marker::Send + std::marker::Sync`                   

My changes (using a lifetime parameter):

pub struct SeparableBssrdfAdapter<'b> {                                                                                                     
    pub bssrdf: &'b SeparableBssrdf,                                                                                                        
}                                                                                                                                           
                                                                                                                                            
impl<'b> SeparableBssrdfAdapter<'b> {                                                                                                       
    pub fn new(bssrdf: &'b SeparableBssrdf) -> Self {                                                                                       
        SeparableBssrdfAdapter {                                                                                                            
            bssrdf: bssrdf,                                                                                                                 
        }                                                                                                                                   
    }                                                                                                                                       
}                                                                                                                                           
                                                                                                                                            
impl<'b> Bxdf for SeparableBssrdfAdapter<'b> {                                                                                              
    fn f(&self, wo: &Vector3f, wi: &Vector3f) -> Spectrum {                                                                                 
        // let mut f: Spectrum = self.bssrdf.sw(wi);                                                                                        
                                                                                                                                            
        // update BSSRDF transmission term to account for adjoint light transport                                                           
                                                                                                                                            
        // if (self.bssrdf.mode == TransportMode::Radiance) {                                                                               
        //     f *= self.bssrdf.eta * self.bssrdf.eta;                                                                                      
        // }                                                                                                                                
        // f                                                                                                                                
        // WORK                                                                                                                             
        Spectrum::default()                                                                                                                 
    }                                                                                                                                       
    fn get_type(&self) -> u8 {                                                                                                              
        BxdfType::BsdfDiffuse as u8 | BxdfType::BsdfReflection as u8                                                                        
    }                                                                                                                                       
}                                                                                                                                           

And here the problematic line:

            bxdfs.push(Arc::new(SeparableBssrdfAdapter::new(                                                                                
                self as &SeparableBssrdf,                                                                                                   
            )));

I do specify the Sync + Send markers elsewhere, e.g. here:

> rg -trust "Arc<Bxdf \+ Send \+ Sync"
...
src/materials/subsurface.rs
130:        let mut bxdfs: Vec<Arc<Bxdf + Send + Sync>> = Vec::new();

But how is the syntax to do so with the reference to the trait object?

Have you tried it? The compiler tells you exactly what to do:

error[E0178]: expected a path on the left-hand side of `+`, not `&ToString`
 --> src/main.rs:2:12
  |
2 |     let a: &ToString + Send + Sync;
  |            ^^^^^^^^^^^^^^^^^^^^^^^ help: try adding parentheses: `&(ToString + Send + Sync)`
1 Like

Have you tried it? The compiler tells you exactly what to do

@jethrogb Thanks, but that approach gave me headache with lifetimes, so I solved the problem slightly differently. Instead of trying to convert &self I just cloned a Arc<TabulatedBssrdf>, which can be passed on as a trait object (Arc<SeparableBssrdf + Sync + Send>), and used three extra parameters to pass on the information needed. See commit for details .... Thanks again, for your suggestions :wink: