Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I convert a `&T` to just `T`?

Tags:

generics

rust

I want to write a function that takes in an array with any type in it and returns the last element of the array, so I've tried:

fn main() {
    let v = ["a", "b"];
    println!("{}", last(&v));
}

fn last<T: Clone>(slice: &[T]) -> &T {
    &slice[slice.len()-1]
}

and that seems to work, but when I apply a small adjustment:

fn main() {
    let v = ["a", "b"];
    println!("{}", last(&v));
}

fn last<T: Clone>(slice: &[T]) -> T {
    &slice[slice.len()-1]
}

Then I'm met with:

error[E0308]: mismatched types
 --> <anon>:9:5
  |
9 |     &slice[n-1]
  |     ^^^^^^^^^^^ expected type parameter, found &T
  |
 = note: expected type `T`
            found type `&T`

How do I convert the &T to just T?

like image 966
UberStuper Avatar asked Jun 09 '17 23:06

UberStuper


1 Answers

In your first example, you are returning a &T and taking a reference to something, so the value and the types match:

fn last<T: Clone>(slice: &[T]) -> &T {
//                                ^^
    &slice[slice.len()-1]
//  ^
}

But, then you said you weren't going to return a reference anymore, but didn't change the implementation.

fn last<T: Clone>(slice: &[T]) -> T {
//                                ^
    &slice[slice.len()-1]
//  ^
}

T, &T and &mut T are all different types from each other! This means it's the same as this "small adjustment":

fn foo() -> i32  { 42 } // Before
fn foo() -> bool { 42 } // After

Let's drop the & from the body:

fn last<T: Clone>(slice: &[T]) -> T {
    slice[slice.len()-1]
}

Oops...

error[E0507]: cannot move out of indexed content
 --> src/main.rs:4:9
  |
4 |         slice[slice.len()-1]
  |         ^^^^^^^^^^^^^^^^^^^^ cannot move out of indexed content

This is well explained in What does "cannot move out of indexed content" mean?.


The answer to your question is: there's no one right way. There are three broad possibilities:

  1. The type implements Copy and the compiler automatically dereferences it for you:

    fn last_copy<T: Copy>(slice: &[T]) -> T {
        slice[slice.len()-1]
    }
    
  2. The type implements Clone, so you can explicitly call Clone to duplicate it:

    fn last_clone<T: Clone>(slice: &[T]) -> T {
        slice[slice.len()-1].clone()
    }
    

    There might also be other methods on your type that do something similar.

  3. You don't. Sometimes, if you have a reference, you can't get a corresponding value. In those cases, you need to re-evaluate your design.

like image 162
Shepmaster Avatar answered Sep 30 '22 14:09

Shepmaster