Recently I played with LazyLock to cache a custom struct with values that can't be const/static and I found that I can't pass LazyLock into a function that uses AsRef<mystruct> instead of simply &mystruct.
(in the example I simply use String instead of a custom struct)
use std::sync::LazyLock;
static CACHED_VALUE: LazyLock<String> = LazyLock::new(|| "This is the cached value".to_owned());
pub fn try_this() {
let value_not_cached = String::from("abc");
print_length(value_not_cached); // works ok
print_length(CACHED_VALUE); // the trait bound LazyLock<std::string::String>: AsRef<str> is not satisfied the trait AsRef<str> is not implemented for LazyLock<std::string::String
}
// stupid example of a function that takes AsRef
pub fn print_length<S>(str: S)
where
S: AsRef<str>,
{
let number = str.as_ref().len();
println!("length is: {number}")
}
This makes sense but I wonder, is there a solution without re-writing functions like print_length that take AsRef<mystruct> or Into<mystruct>.
In general, I think it's an anti-pattern to take AsRef or Into arguments in the vast majority of cases. You can just take a reference.
use std::sync::LazyLock;
static CACHED_VALUE: LazyLock<String> = LazyLock::new(|| "This is the cached value".to_owned());
pub fn try_this() {
let value_not_cached = String::from("abc");
print_length(&value_not_cached);
print_length(&CACHED_VALUE);
}
pub fn print_length(str: &str) {
let number = str.len();
println!("length is: {number}")
}
Yes, you need an & to call it, but I think that's not a problem. The & shows to the reader that print_length does not take ownership of the argument, so having it here brings value to the reader.