RSSAmplifier

Arthur::Carcano · Jan 10, 2021

Rust gotcha: deriving Copy and Clone can be too restrictive

0
Sign in to vote or save

This site does not allow itself to be embedded. You can still read it on the original site — the toolbar below keeps your place in the directory.

This is a very short post to share a Rust gotcha I recently discovered. It can be summed up in this TL;DR , freely adapted from the doc: For a generic struct, #[derive] implements Clone (resp. Copy) conditionally by adding bound Clone (resp. Copy) on generic parameters, which isn't always desired. The problematic situation can be seen in the following code: #[derive( Clone , Copy )] struct…

This is a very short post to share a Rust gotcha I recently discovered. It can be summed up in this TL;DR</em>, freely adapted from the doc:</p>

For a generic struct, #[derive]</code> implements Clone (resp. Copy) conditionally by adding bound Clone (resp. Copy) on generic parameters, which isn't always desired.</p> </blockquote>

The problematic situation can be seen in the following code:</p>

#[derive(</span>Clone</span>,</span> Copy</span>)]</span></span>
struct</span> MyRef</span><'</span>a</span>,</span>T</span>>(</span>&</span>'</span>a T</span>);</span></span>
</span>
fn</span> main</span>() {</span></span>
    // v is not Copy, but &v is</span></span>
    let</span> v</span> :</span> Vec</span><()></span> =</span> Vec</span>::</span>new</span>();</span></span>
    // _r1 = &v; would compile</span></span>
    let</span> _r1</span> =</span> MyRef</span>(</span>&</span>v);</span></span>
    // _r1 moves into _r2</span></span>
    // except if _r1 is copy</span></span>
    let</span> _r2</span> =</span> _r1;</span></span>
    // Error: use of moved value: _r1</span></span>
    _r1;</span></span>
}</span></span></code></pre>

We would expect MyRef<'a,T></code> to be Clone and Copy even if T</code> is not Clone or Copy, because it only contains &'a T</code> which is always Copy and Clone. But what the Rust compiler is deriving, is instead:</p>

struct</span> MyRef</span><'</span>a</span>,</span> T</span>>(</span>&</span>'</span>a T</span>);</span></span>
</span>
impl</span><'</span>a</span>,</span> T</span>></span> Clone</span> for</span> MyRef</span><'</span>a</span>,</span> T</span>></span></span>
// Notice this bound</span></span>
where</span></span>
    T</span>:</span> Clone</span>,</span></span>
{</span></span>
    fn</span> clone</span>(</span>&</span>self)</span> -></span> Self {</span></span>
        MyRef</span>((</span>&</span>self</span>.</span>0</span>)</span>.</span>clone</span>())</span></span>
    }</span></span>
}</span></span>
</span>
impl</span><'</span>a</span>,</span> T</span>></span> Copy</span> for</span> MyRef</span><'</span>a</span>,</span> T</span>> </span></span>
// Notice this bound</span></span>
where</span> T</span>:</span> Copy</span> {}</span></span></code></pre>

In this case, we should rather implement Clone and Copy by hand, and remove the unneeded bounds like so:</p>

struct</span> MyRef</span><'</span>a</span>,</span> T</span>>(</span>&</span>'</span>a T</span>);</span></span>
</span>
impl</span><'</span>a</span>,</span> T</span>></span> Clone</span> for</span> MyRef</span><'</span>a</span>,</span> T</span>></span></span>
{</span></span>
    fn</span> clone</span>(</span>&</span>self)</span> -></span> Self {</span></span>
        MyRef</span>((</span>&</span>self</span>.</span>0</span>)</span>.</span>clone</span>())</span></span>
    }</span></span>
}</span></span>
</span>
impl</span><'</span>a</span>,</span> T</span>></span> Copy</span> for</span> MyRef</span><'</span>a</span>,</span> T</span>></span></span>
{}</span></span></code></pre>

Read on ngr.yt

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.