Flexible Input Parameters: From, AsRef
In this article, I’ll be covering two built-in traits that make your life in Rust easier:
AsRef
Firstly, AsRef<T> is to specify the parameter as “anything that you can borrow type T from”. Take the following example:
fn take_str(st:String){
println!("{}",st);
}
fn take_str_reference(st:&String){
println!("{}",st);
}
fn take_str_literal(st:&str){
println!("{}",st);
}
Each takes a different type, String, &String, &str, respectively. When you look at the use case of the function, we don’t actually need full ownership of value. So the common denominator among them is we just need to borrow the value. The problem is, Rust allows putting input, the type of which is stated as specified in the function signature. Let’s look at the following example.
take_str("string".to_string()); // Ok
take_str_reference("string".to_string()); //Error!
take_str_literal("string".to_string())// Error!
take_str(&"string".to_string()); // Error
take_str_reference(&"string".to_string()); //OK
take_str_literal(&"string".to_string())// Error!
take_str("string"); // Error!
take_str_reference("string"); // Error!
take_str_literal("string")// Ok
So, how can we impart some flexibility to the function? That’s what AsMut is for!
领英推荐
We change the function signature from specific to general, AsMut<str>.
fn take_str(st:impl AsRef<str> + std::fmt::Display){
println!("{}",st.as_ref());
}
Note that we put one more trait bound, std::fmt::Display to enable printing. This time, when you pass any string variants, String, &String, &str, Rust compiler is okay with them.
Wait, then, how can you get &str from String? It deserves its own writing but simply put, there is an interplay between AsRef and Deref.
From
Well, in a way, the use of From is somewhat similar to AsRef in that it enables converting a value from one to the other. The difference is that From and its blanket implementation, Into is used when you want to take ownership of the parameter. Let’s how it is used when you implement NewType Pattern.
#[derive(Debug)
struct VecWrapper<T>(Vec<T>); //new type pattern
impl<T> From<VecWrapper<T>> for Vec<T> {
// But still, you want this to be treated as Vec of type T.
// When it is passed as argument
? ? fn from(w:VecWrapper<T>)-> Vec<T>{?
? ? ? ? w.0
? ? }
}
fn function_taking_vec<T: std::fmt::Debug>(arr : impl Into<Vec<T>>){
? ? println!("{:?}",arr.into()); // into() is applied.
}
fn main(){
? ? let vc = VecWrapper(vec!["Migo".to_string()]);
? ? function_taking_vec(vc); // you simply pass VecWrapper, not Vec?
}]