Search code examples
rust

what is the different with Option as_ref and as_deref in Rust


Now I am using the rust code snippet like this:

let stackoverflow = format!("{}{}{}","\\social[stackoverflow]{",request.cv_main.stackoverflow.as_deref().unwrap_or_default(),"}\n");

and this is the request cv_main defined:

use serde::{Deserialize, Serialize};

use super::{edu::edu::CvEduResp, work::cv_work_resp::CvWorkResp};

#[derive(Serialize, Deserialize, Default, Clone)]
pub struct CvMainResp {
    pub id: i64,
    pub cv_name: String,
    pub created_time: i64,
    pub updated_time: i64,
    pub user_id: i64,
    pub cv_status: i32,
    pub template_id: i64,
    pub employee_name: Option<String>,
    pub birthday: Option<String>,
    pub phone: Option<String>,
    pub email: Option<String>,
    pub stackoverflow: Option<String>,
    pub github: Option<String>,
    pub blog: Option<String>,
    pub edu: Option<Vec<CvEduResp>>,
    pub work: Option<Vec<CvWorkResp>>,
}

this code works fine, but I am still could not understand why here need to use as_deref(). I have read the article https://www.fpcomplete.com/blog/rust-asref-asderef/, still did not figure out. does there any simple explain the different with as_ref and as_deref?


Solution

  • as_ref() takes an &Option<String> and converts it to Option<&String>. This is not the same type as Option<&str>, the difference being that Option<&String> always refers to an actual String, whereas Option<&str> can refer to string data that is just part of a String or that is not stored in a heap-allocated String at all (but in a static constant, for example). For this reason APIs that take strings are encouraged to accept &str or Option<&str> rather than &String or Option<&String>. Fortunately, when you have an Option<&String>, you can always convert it to Option<&str> (but not the other way around).

    To convert Option<&String> returned by as_ref() to Option<&str>, you can call opt.as_ref().map(|s| s.as_str()). String exposes as_str() through Deref, which is why you can call all str methods on a String. Deref exposes the string slice through the a bit awkward &*s syntax, so another way to obtain Option<&str> is opt.as_ref().map(|s| &*s). This will work in generic code where as_str() is not available. Option::as_deref() is a short-hand for that - called on Option<String>, it will return Option<&str>. Called on Option<Vec<u8>> will return Option<&[u8]>, and so on.

    While Option<&String> and Option<&str> are functionally similar, they are not exactly the same. Here the point of as_deref() is to be able to chain it to .unwrap_or_default(). unwrap_or_default() can be called on Option<&str> because &str implements Default, <&str as Default>::default() just returns the reference to a statically allocated "" singleton. On the other hand, Default is not implemented for &String, because it would have to return a reference to a String that outlives it, which is not allowed by the borrow checker.