通用寿命参数和本地范围

发布于 2025-01-18 13:42:26 字数 8013 浏览 2 评论 0原文

Piet是一个带有通用后端的图形库(例如,开罗)。

我想拥有一个特质可渲染的,可以呈现到任何彼得后端(= context)。

pub trait Renderable {
    fn render<C: piet::RenderContext>(&self, ctx: &mut C);

    // Since `Renderable` can render to any `RenderContext`, it can render to a file too.
    fn save_to_file(&self, path: &Path) {
        let ctx = build_cairo_context_for_image_file(path);
        self.render::<piet_common::CairoRenderContext>(&mut ctx);
    }
}

但是,rustc抱怨渲染&lt; c&gt;的对象不安全,所以我使特质本身本身通用:

pub trait Renderable<C: piet::RenderContext> {
    fn render(&self, ctx: &mut C);
    fn save_to_file(&self, path: &Path) -> Result<(), piet_common::Error> {
        let width = 512;
        let height = 512;
        let pix_scale = 1.0;
        let mut device = piet_common::Device::new()?;
        let mut bitmap: piet_common::BitmapTarget =
            device.bitmap_target(width, height, pix_scale)?;
        let mut rc = bitmap.render_context();
        // Renderable::<CairoRenderContext>::render(self, &mut rc);
        self.render(&mut rc);
        rc.finish()?;
        bitmap.save_to_file(path);

        Ok(())
    }
}

现在问题是,当save> save_to_to_file调用self.render(&amp)(&amp) ; mut rc),它无法找到该方法,因为save_to_file是针对c而不是cairorenderContext(尽管cairorenderContercontextext)实施RenderContext)。

    |
 14 | pub trait Renderable<C: piet_common::RenderContext> {
    |                      - this type parameter
 ...
 25 |         self.render(&mut rc);
    |                     ^^^^^^^ expected type parameter `C`, found struct `CairoRenderContext`
    |
    = note: expected mutable reference `&mut C`
               found mutable reference `&mut CairoRenderContext<'_>`

作为尴尬的解决方法,我添加了针对任何类型的渲染&lt; cairorendercontext&gt;实现的特征。问题是cairorenderContext具有生命周期参数,struct cairorendercontext&lt;'a&gt;

pub trait Renderable<C: piet_common::RenderContext> {
    fn render(&self, ctx: &mut C);
}

pub trait Drawable {
    fn save_to_file<'a>(&self, path: &Path) -> Result<(), piet_common::Error>
    where
        Self: Renderable<CairoRenderContext<'a>>,
    {
        let width = 512;
        let height = 512;
        let pix_scale = 1.0;
        let mut device = piet_common::Device::new()?;
        let mut bitmap: piet_common::BitmapTarget =
            device.bitmap_target(width, height, pix_scale)?;
        let mut rc = bitmap.render_context();
        Renderable::<CairoRenderContext>::render(self, &mut rc);
        // self.render(&mut rc);
        rc.finish()?;
        drop(rc);
        bitmap.save_to_file(path);

        // at this point, we don't need device, bitmap or rc.
        // rustc thinks they should be alive.
        Ok(())
    }
}

Lifetime 'a应该代表本地变量设备设备bitmap的rustc错误变量应寿命足够长,以便'a


 1  error[E0597]: `device` does not live long enough                                                     
    |                                                                                                    
    |     fn save_to_file<'a>(&self, path: &Path) -> Result<(), piet_common::Error>                      
    |                     -- lifetime `'a` defined here                                                  
 ...                                                                                                     
    |             device.bitmap_target(width, height, pix_scale)?;                                       
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
    |         let mut rc = bitmap.render_context();                                                      
    |                      ----------------------- assignment requires that `device` is borrowed for `'a`
 ...                                                                                                     
    |     }                                                                                              
    |     - `device` dropped here while still borrowed                                                   
                                                                                                
 2  error[E0597]: `bitmap` does not live long enough                                                     
    |                                                                                                    
    |     fn save_to_file<'a>(&self, path: &Path) -> Result<(), piet_common::Error>                      
    |                     -- lifetime `'a` defined here                                                  
 ...                                                                                                     
    |         let mut rc = bitmap.render_context();                                                      
    |                      ^^^^^^^^^^^^^^^^^^^^^^^                                                       
    |                      |                                                                             
    |                      borrowed value does not live long enough                                      
    |                      assignment requires that `bitmap` is borrowed for `'a`                        
 ...                                                                                                     
    |     }                                                                                              
    |     - `bitmap` dropped here while still borrowed                                                   
                                                                                                
 3  error[E0505]: cannot move out of `bitmap` because it is borrowed                                     
    |                                                                                                    
    |     fn save_to_file<'a>(&self, path: &Path) -> Result<(), piet_common::Error>                      
    |                     -- lifetime `'a` defined here                                                  
 ...                                                                                                     
    |         let mut rc = bitmap.render_context();                                                      
    |                      -----------------------                                                       
    |                      |                                                                             
    |                      borrow of `bitmap` occurs here                                                
    |                      assignment requires that `bitmap` is borrowed for `'a`                        
 ...                                                                                                     
    |         bitmap.save_to_file(path);                                                                 
    |         ^^^^^^ move out of `bitmap` occurs here                     
  • Q1。当Rustc关于对象安全的错误时,是从渲染&lt; c&gt; to 渲染&lt; c&gt;中升高类型参数的正确解决方案吗?
  • Q2。我应该有两个特征(可渲染的,可绘制的),所以save_to_file可以调用渲染,还是有更好的方法?
  • Q3。我如何正确地告诉Rustc关于self:在最后一个示例中渲染&lt; cairorendercontext&lt;

piet is a drawing library with generic backends (cairo, for example).

I want to have a trait Renderable that can render to any piet backend (=context).

pub trait Renderable {
    fn render<C: piet::RenderContext>(&self, ctx: &mut C);

    // Since `Renderable` can render to any `RenderContext`, it can render to a file too.
    fn save_to_file(&self, path: &Path) {
        let ctx = build_cairo_context_for_image_file(path);
        self.render::<piet_common::CairoRenderContext>(&mut ctx);
    }
}

However rustc complained about object unsafety of render<C>, so I made the trait itself generic:

pub trait Renderable<C: piet::RenderContext> {
    fn render(&self, ctx: &mut C);
    fn save_to_file(&self, path: &Path) -> Result<(), piet_common::Error> {
        let width = 512;
        let height = 512;
        let pix_scale = 1.0;
        let mut device = piet_common::Device::new()?;
        let mut bitmap: piet_common::BitmapTarget =
            device.bitmap_target(width, height, pix_scale)?;
        let mut rc = bitmap.render_context();
        // Renderable::<CairoRenderContext>::render(self, &mut rc);
        self.render(&mut rc);
        rc.finish()?;
        bitmap.save_to_file(path);

        Ok(())
    }
}

Now the problem is, when save_to_file calls self.render(&mut rc), it cannot find the method because save_to_file is implemented for C and not CairoRenderContext (although CairoRenderContext implements RenderContext).

    |
 14 | pub trait Renderable<C: piet_common::RenderContext> {
    |                      - this type parameter
 ...
 25 |         self.render(&mut rc);
    |                     ^^^^^^^ expected type parameter `C`, found struct `CairoRenderContext`
    |
    = note: expected mutable reference `&mut C`
               found mutable reference `&mut CairoRenderContext<'_>`

As an awkward workaround, I added trait Drawable that is implemented for any type of Renderable<CairoRenderContext>. The problem is CairoRenderContext has a lifetime parameter, struct CairoRenderContext<'a>.

pub trait Renderable<C: piet_common::RenderContext> {
    fn render(&self, ctx: &mut C);
}

pub trait Drawable {
    fn save_to_file<'a>(&self, path: &Path) -> Result<(), piet_common::Error>
    where
        Self: Renderable<CairoRenderContext<'a>>,
    {
        let width = 512;
        let height = 512;
        let pix_scale = 1.0;
        let mut device = piet_common::Device::new()?;
        let mut bitmap: piet_common::BitmapTarget =
            device.bitmap_target(width, height, pix_scale)?;
        let mut rc = bitmap.render_context();
        Renderable::<CairoRenderContext>::render(self, &mut rc);
        // self.render(&mut rc);
        rc.finish()?;
        drop(rc);
        bitmap.save_to_file(path);

        // at this point, we don't need device, bitmap or rc.
        // rustc thinks they should be alive.
        Ok(())
    }
}

The lifetime 'a is supposed to represent the lifetime of a local variable device but rustc errors that device and bitmap variables should live long enough for 'a.


 1  error[E0597]: `device` does not live long enough                                                     
    |                                                                                                    
    |     fn save_to_file<'a>(&self, path: &Path) -> Result<(), piet_common::Error>                      
    |                     -- lifetime `'a` defined here                                                  
 ...                                                                                                     
    |             device.bitmap_target(width, height, pix_scale)?;                                       
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
    |         let mut rc = bitmap.render_context();                                                      
    |                      ----------------------- assignment requires that `device` is borrowed for `'a`
 ...                                                                                                     
    |     }                                                                                              
    |     - `device` dropped here while still borrowed                                                   
                                                                                                
 2  error[E0597]: `bitmap` does not live long enough                                                     
    |                                                                                                    
    |     fn save_to_file<'a>(&self, path: &Path) -> Result<(), piet_common::Error>                      
    |                     -- lifetime `'a` defined here                                                  
 ...                                                                                                     
    |         let mut rc = bitmap.render_context();                                                      
    |                      ^^^^^^^^^^^^^^^^^^^^^^^                                                       
    |                      |                                                                             
    |                      borrowed value does not live long enough                                      
    |                      assignment requires that `bitmap` is borrowed for `'a`                        
 ...                                                                                                     
    |     }                                                                                              
    |     - `bitmap` dropped here while still borrowed                                                   
                                                                                                
 3  error[E0505]: cannot move out of `bitmap` because it is borrowed                                     
    |                                                                                                    
    |     fn save_to_file<'a>(&self, path: &Path) -> Result<(), piet_common::Error>                      
    |                     -- lifetime `'a` defined here                                                  
 ...                                                                                                     
    |         let mut rc = bitmap.render_context();                                                      
    |                      -----------------------                                                       
    |                      |                                                                             
    |                      borrow of `bitmap` occurs here                                                
    |                      assignment requires that `bitmap` is borrowed for `'a`                        
 ...                                                                                                     
    |         bitmap.save_to_file(path);                                                                 
    |         ^^^^^^ move out of `bitmap` occurs here                     
  • Q1. When rustc errors about object-safety, was it a correct solution to uplift a type parameter from render<C> to Renderable<C>?
  • Q2. Should I have two traits (Renderable, Drawable) so save_to_file can call render or is there a better way?
  • Q3. How can I correctly tell rustc about Self: Renderable<CairoRenderContext<_>> in the last example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

油焖大侠 2025-01-25 13:42:26

关于&amp; mut c&amp; mut cairorenderContext&lt;'_&gt;不匹配的第二个错误是通过省略save_to_to_file()从性状定义中,而是在Inplb块中定义它。这是我的意思是一个高度简化的版本:

pub trait Renderable<C: RenderContext> {
    fn render(&self, ctx: &mut C);
    fn save_to_file(&self);
}

struct Cairo;

impl Renderable<CairoRenderContext<'_>> for Cairo {
    fn render(&self, ctx: &mut CairoRenderContext<'_>) {
        todo!()
    }

    fn save_to_file(&self) {
        let mut rc = CairoRenderContext::new();
        self.render(&mut rc);
    }
}

原始方法在概念上似乎是错误的,因为可渲染的特征应该是后端-Agnostic,但是save_to_file()正在使用特定于开罗的类型,从而导致误差。


Q1

将通用性从方法转移到特征确实是制造特质对象安全的一种策略。其他策略是

  • 使该方法使用动态调度来将其参数
  • 标记为where self:大小(尽管您将无法使用动态调度来调用该方法)

Q2

您不需要两个特征如果您使用了我描述的方法。

Q3

答案是a 更高排名的性状界限

pub trait Drawable {
    fn save_to_file(&self, path: &Path) -> ...
    where
        Self: for<'a> Renderable<CairoRenderContext<'a>>,
    {...}
}

注意,您可以使界限成为超级框架:

pub trait Drawable: for<'a> Renderable<CairoRenderContext<'a>> {
    fn save_to_file(&self, path: &Path) -> ... {...}
}

Your second error, regarding the &mut C and &mut CairoRenderContext<'_> mismatch, is solved by omitting the body of save_to_file() from the trait definition, and instead defining it in the impl block. Here's a highly simplified version of what I mean:

pub trait Renderable<C: RenderContext> {
    fn render(&self, ctx: &mut C);
    fn save_to_file(&self);
}

struct Cairo;

impl Renderable<CairoRenderContext<'_>> for Cairo {
    fn render(&self, ctx: &mut CairoRenderContext<'_>) {
        todo!()
    }

    fn save_to_file(&self) {
        let mut rc = CairoRenderContext::new();
        self.render(&mut rc);
    }
}

The original approach seems conceptually wrong in that the Renderable trait is supposed to be backend-agnostic, yet the default impl of save_to_file() was using types specific to Cairo, thus causing the error.


Q1

Moving the generic from the method to the trait is indeed one strategy to make a trait object-safe. Other strategies are

  • Making the method use dynamic dispatch for its arguments
  • Marking the method with where Self: Sized (though you won't be able to call that method using dynamic dispatch)

Q2

You don't need two traits if you used the approach I described.

Q3

The answer is a higher-ranked trait bound:

pub trait Drawable {
    fn save_to_file(&self, path: &Path) -> ...
    where
        Self: for<'a> Renderable<CairoRenderContext<'a>>,
    {...}
}

Note you can make the bound a supertrait instead:

pub trait Drawable: for<'a> Renderable<CairoRenderContext<'a>> {
    fn save_to_file(&self, path: &Path) -> ... {...}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文