Confused on why the Rust compiler disagrees with this implementation

I think this is an abitrary and unneccasary restriction within the Rust compiler. You can implement a method within a trait. This method is a 'Material' and thus &self only must map to an &dyn Material to execute the method properly. Now I get you can't have a generic unimplemented trait method because you have to guarantee everything that implements that trait must have a generic mapped method or else you might not be able to dispatch it correctly. A method in a separate dll could fail since it wouldn't know to implement some generic versions then when it is passed in as a vtable it could fail.

However, an implemented generic method will work with any &dyn Material. Therefore you can always know what method to dispatch to. It doesn't EVER need to use a vtable dispatch. It's already implemented to take any &dyn Material.

Is there a reason I'm not understanding why this is 'bad'? This seems just like a silly restriction that solves no problems. Is there a place to make a proposal for implemented generic trait methods? Seems like a powerful addition to traits.

pub trait Material : Sync + Send
{
    fn as_vulkan_material(&self) -> &VulkanMaterial;
    fn set_value_by_ptr(&self, name: &str, ptr: *const u8, size: usize);
    fn upload_uniforms(&self);

    fn set_matrix(&self, name: &str, matrix: Mat4) {
        unsafe 
        {
            let ptr = std::mem::transmute::<*const Mat4,*const u8>(&matrix as *const Mat4);
            self.set_value_by_ptr(name, ptr, std::mem::size_of::<Mat4>())
        }
    }

    fn set_values<T: Sized + Copy>(&self, name: &str, value: T) {
        unsafe 
        {
            let ptr = std::mem::transmute::<*const T,*const u8>(&value as *const T);
            self.set_value_by_ptr(name, ptr, std::mem::size_of::<T>())
        }    
    }
}
error[E0038]: the trait `Material` cannot be made into an object
  --> rcubed\src\rendering\material.rs:39:61
   |
39 |     pub fn create(options: &MaterialCreationOptions) -> Arc<dyn Material> {
   |                                                             ^^^^^^^^^^^^ `Material` cannot be made into an object
   |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> rcubed\src\rendering\material.rs:19:8
   |
5  | pub trait Material : Sync + Send
   |           -------- this trait cannot be made into an object...
...
19 |     fn set_values<T: Sized + Copy>(&self, name: &str, value: T) 
   |        ^^^^^^^^^^ ...because method `set_values` has generic type parameters
   = help: consider moving `set_values` to another trait


I'm not sure what error you're talking about. Can you link to a playground that reproduces the problem?

Debugging by divination: if you want a method to only work on &dyn Material, then you might want to put it in impl dyn Material + '_ { … }. Because that's how you get something where "&self only must map to an &dyn Material" -- methods in the trait definition itself work subtly differently from that.

I think your complaint is that since your trait supplies a default implementation for a generic method, the trait should still be object safe.

The problem with that is that types implementing the trait can still override the default implementation, which would cause a static and dynamic call to that method to have different behavior even on the same value.

1 Like

Ah, the discord told me you have to specify where Self : Sized to disconnect the method from the trait.

    fn set_values<T: Sized + Copy>(&self, name: &str, value: T) 
        where Self : Sized
    {
        unsafe 
        {
            let ptr = std::mem::transmute::<*const T,*const u8>(&value as *const T);
            self.set_value_by_ptr(name, ptr, std::mem::size_of::<T>())
        }    
    }

so rust DOES support this just some extra hoops you have to jump through.

The problem with that is that types implementing the trait can still override the default implementation, which would cause a static and dynamic call to that method to have different behavior even on the same value.

ah ok that's why you have to specify something to 'disconnect' it as the discord put it. It COULD become a dispatched method if it is overridden. Only been doing Rust for a few weeks and that concept hadn't occurred to me.

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.