How do I cast String to &str?

Sorry for the noob question. But I need to use &str, but can't find a way to concatenate it to return a value. I don't know how to implement the as_str() or just & here as instructed here.

 fn description(&self) -> &str {
        let mut description = String::new();
        description.push_str(&self.artist);
        description.push_str(&self.title);
        description.push_str(&self.current_price.to_string());
        return description.as_str();
    }
error[E0515]: cannot return value referencing local variable `description`
  --> main.rs:42:16
   |
42 |         return description.as_str();
   |                -----------^^^^^^^^^
   |                |
   |                returns a value referencing data owned by the current function
   |                `description` is borrowed here

For more information about this error, try `rustc --explain E0515`.
error: could not compile `src` (bin "src") due to 1 previous error

you cannot do that. if you can, a reasonable solution would be to store the full description in Self, but if you cannot you should fall back to returning a String instead.

&s must borrow from something that exist.
if you have fn description(&self) -> &str, then the &str must belong to self

You cannot return a reference to a local variable because the reference will be invalid once the function returns. Just return the String and take the reference in the calling code:

fn description(&self) -> String {
    let mut description = String::new();
    description.push_str(&self.artist);
    description.push_str(&self.title);
    description.push_str(&self.current_price.to_string());
    description
}

// Then use it as:
let ref: &str = obj.description().as_str(); 

You don't need to add the type when calling as_str(): I added it just to be explicit about what's happening.

you cannot do that. if you can, a reasonable solution would be to store the full description in Self, but if you cannot you should fall back to returning a String instead. &s must borrow from something that exist. if you have fn description(&self) -> &str, then the &str must belong to self

I had to use String, because I was not allowed to add a extra field.

This was my final solution:

 fn description(&self) -> String {
        let mut description = String::new();
        description.push_str(&self.artist);
        description.push_str(", ");
        description.push_str(&self.title);
        description.push_str(", ");
        description.push_str(&self.current_price.to_string());
        return description;
    }

I also had to change some other code and change some uses to .clone().

You cannot return a reference to a local variable because the reference will be invalid once the function returns. Just return the String and take the reference in the calling code. You don't need to add the type when calling as_str(): I added it just to be explicit about what's happening.

Thank you both for the help and explanation.

If you expected to use only references (you wanted to return a &str) then it is a bit strange that you had to add calls to .clone(). Anyway, without the full code is difficult to understand exactly what you were trying to do.

A tip for the big picture: any time you find yourself building and returning a string, you should think about whether you should, instead, implement fmt::Display on something. But it sounds like you’re doing some early exercises, so don’t worry about getting this right yet.

Original: Comparison on a `Box<(dyn Valueable + 'static)>` fails - #6 by canina
Current code:

Updated

use std::cmp::Ordering;

#[derive(Clone)]
struct Painting {
    artist: String,
    title: String,
    current_price: f32,
}

#[derive(Clone)]
struct Car {
    brand: String,
    model: String,
    original_price: f32,
    age: u8,
}

pub trait Valueable {
    fn price(&self) -> f32;
    fn description(&self) -> String;
}

impl Valueable for Car {
    fn price(&self) -> f32 {
         return self.original_price * 0.95_f32.powf(self.age.into());
    }
    fn description(&self) -> String {
        let mut description = String::new();
        description.push_str(&self.brand);
        description.push_str(", ");
        description.push_str(&self.model);
        description.push_str(", ");
        description.push_str(&self.price().to_string());
        description.push_str(", ");
        description.push_str(&self.age.to_string());
        return description;
    }
}

impl Valueable for Painting {
    fn price(&self) -> f32 {
        return self.current_price;
    }

    fn description(&self) -> String {
        let mut description = String::new();
        description.push_str(&self.artist);
        description.push_str(", ");
        description.push_str(&self.title);
        description.push_str(", ");
        description.push_str(&self.current_price.to_string());
        return description;
    }
}

pub enum BST {
    Leaf {
        value: Box<dyn Valueable>,
        left: Box<BST>,
        right: Box<BST>,
    },
    Empty,
}



impl BST {
    pub fn new() -> Self {
        BST::Empty
    }

    pub fn add(&mut self, new_value: Box<dyn Valueable>) {
        match self {
            BST::Leaf {
                value,
                left,
                right,
            } => match new_value.price().partial_cmp(&value.price() ) {
            Some(Ordering::Less) => left.add(new_value),
            Some(Ordering::Greater) => right.add(new_value),
            Some(Ordering::Equal) => return,
            None => panic!("At least one price is NaN"),
        },
        BST::Empty => {
            *self = BST::Leaf {
                value: new_value,
                left: Box::new(BST::Empty),
                right: Box::new(BST::Empty),
            }
        }
    }
    }

    pub fn contains(&self, find_value: Box<dyn Valueable>) -> bool {
        match self {
            BST::Leaf {
                value,
                left,
                right,
            } => match find_value.price().total_cmp(&value.price()) {
                Ordering::Less => left.contains(find_value),
                Ordering::Greater => right.contains(find_value),
                Ordering::Equal => true,
            },
            BST::Empty => false,
        }
    }

    pub fn as_vector(&mut self) -> Vec::<String> {
        match self {
            BST::Empty => return Vec::new(),
            BST::Leaf { value, left ,right } => {
                let mut values = right.as_vector();
                values.push(value.description());
                values.extend(left.as_vector());
                values
            },
        }
    }
}


fn main(){
    let mut bst = BST::new();

     let car1 = Car {
       brand: "Brand".to_string(),
       model: "model".to_string(),
       original_price: 100000.75,
       age: 3,
   };

    BST::add(&mut bst, Box::new(car1.clone()));

     let painting1 = Painting {
       artist: "Artist".to_string(),
       title: "Title".to_string(),
       current_price: 100.50,
   };

    BST::add(&mut bst, Box::new(painting1));

    println!("{:?}", BST::contains(&mut bst, Box::new(car1.clone())));
    println!("{:?}", BST::as_vector(&mut bst));
}

I will link code in full from no on, thank you for reminding me.

That is a better way to do it indeed. I saw it passing by, but thought pushing strings was the best way. I will keep it in mind!

Even when returning String, I suggest:

    fn description(&self) -> String {
        format!(
            "{}, {}, {}, {}",
            self.brand,
            self.model,
            self.price(),
            self.age
        )
    }

Or if you're not up to read it all right now, the most common format str argument specifiers are

  • "{}" to use Display on the next parameter (usually human friendly)
  • "{:?}" to use Debug on the next parameter (most types implement this)
  • "{variable}" or "{variable:?}" to use Display or Debug on a variable instead of another parameter

Thank you for the example. I will do it that way from now, it looks cleaner too. And it is simialr to what I am used to from Python.

if you have fn description(&self) -> &str, then the &str must belong to self

&str could also come from a static, const, or a memory leak such as Box::leak (but please don't use the last one unless you have a very good reason)

Arguably if we're going there for completeness, you should also mention that if Self has a lifetime parameter it could be from a member reference with that lifetime too, but we're getting a bit too into the weeds for this level of question :face_savoring_food: