Derive Macro derive_more::AsRef

source ·
#[derive(AsRef)]
{
    // Attributes available to this derive:
    #[as_ref]
}
Available on crate feature as_ref only.
Expand description

What #[derive(AsRef)] generates

Deriving AsRef generates one or more implementations of AsRef, each corresponding to one of the fields of the decorated type. This allows types which contain some T to be passed anywhere that an AsRef<T> is accepted.

Newtypes and Structs with One Field

When AsRef is derived for a newtype or struct with one field, a single implementation is generated to expose the underlying field.

#[derive(AsRef)]
struct MyWrapper(String);

Generates:

impl AsRef<String> for MyWrapper {
    fn as_ref(&self) -> &String {
        &self.0
    }
}

It’s also possible to use the #[as_ref(forward)] attribute to forward to the as_ref implementation of the field. So here SingleFieldForward implements all AsRef for all types that Vec<i32> implements AsRef for.

#[derive(AsRef)]
#[as_ref(forward)]
struct SingleFieldForward(Vec<i32>);

let item = SingleFieldForward(vec![]);
let _: &[i32] = (&item).as_ref();

This generates code equivalent to:

impl<T: ?Sized> AsRef<T> for SingleFieldForward
where
    Vec<i32>: AsRef<T>,
{
    #[inline]
    fn as_ref(&self) -> &T {
        self.0.as_ref()
    }
}

Specifying concrete types, to derive impls for, is also supported via #[as_ref(<types>)] attribute. These types can include both the type of the field itself, and types for which the field type implements AsRef.

#[derive(AsRef)]
#[as_ref(str, [u8], String)]
struct Types(String);

let item = Types("test".to_owned());
let _: &str = item.as_ref();
let _: &[u8] = item.as_ref();
let _: &String = item.as_ref();

WARNING: When either the field type, or the specified conversion type, contains generic parameters, they are considered as the same type only if are named string-equally, otherwise are assumed as different types even when represent the same type in fact (type aliases, for example).

#[derive(AsRef)]
#[as_ref(i32)] // generates `impl<T: AsRef<i32>> AsRef<i32> for Generic<T>`
struct Generic<T>(T);

#[derive(AsRef)]
#[as_ref(T)] // generates `impl<T> AsRef<T> for Transparent<T>`
struct Transparent<T>(T);

#[derive(AsRef)]
// #[as_ref(RenamedVec<T>)] // not supported, as types are not named string-equally
struct Foo<T>(Vec<T>);
type RenamedVec<T> = Vec<T>;

#[derive(AsRef)]
#[as_ref(RenamedString)] // generates `impl AsRef<RenamedString> for Bar`,
struct Bar(String);      // as generics are not involved
type RenamedString = String;

Generating code like this is not supported:

struct Generic<T>(T);

impl AsRef<i32> for Generic<i32> {
    fn as_ref(&self) -> &i32 {
        &self.0
    }
}

Structs with Multiple Fields

When AsRef is derived for a struct with more than one field (including tuple structs), you must also mark one or more fields with the #[as_ref] attribute. An implementation will be generated for each indicated field.

#[derive(AsRef)]
struct MyWrapper {
    #[as_ref(str)]
    name: String,
    #[as_ref]
    num: i32,
    valid: bool,
}

Generates:

impl AsRef<str> for MyWrapper {
    fn as_ref(&self) -> &str {
        self.name.as_ref()
    }
}

impl AsRef<i32> for MyWrapper {
    fn as_ref(&self) -> &i32 {
        &self.num
    }
}

Tuples (not supported)

Only conversions that use a single field are possible with this derive. Something like this wouldn’t work, due to the nature of the AsRef trait itself:

#[derive(AsRef)]
#[as_ref((str, [u8]))]
struct MyWrapper(String, Vec<u8>)

If you need to convert into a tuple of references, consider using the Into derive with #[into(ref)].

Skipping

Or vice versa: you can exclude a specific field by using #[as_ref(skip)] (or #[as_ref(ignore)]). Then, implementations will be generated for non-indicated fields.

#[derive(AsRef)]
struct MyWrapper {
    #[as_ref(skip)]
    name: String,
    #[as_ref(ignore)]
    num: i32,
    valid: bool,
}

Generates:

impl AsRef<bool> for MyWrapper {
    fn as_ref(&self) -> &bool {
        &self.valid
    }
}

Coherence

Note that AsRef<T> may only be implemented once for any given type T. This means any attempt to mark more than one field of the same type with #[as_ref] will result in a compilation error.

// Error! Conflicting implementations of AsRef<String>
#[derive(AsRef)]
struct MyWrapper {
    #[as_ref]
    str1: String,
    #[as_ref]
    str2: String,
}

Similarly, if some field is annotated with #[as_ref(forward)], no other field can be marked.

// Error! Conflicting implementations of `AsRef<i32>`
// note: upstream crates may add a new impl of trait `AsRef<i32>`
// for type `String` in future versions
#[derive(AsRef)]
struct ForwardWithOther {
    #[as_ref(forward)]
    str: String,
    #[as_ref]
    number: i32,
}

Multiple forwarded impls with different concrete types, however, can be used.

#[derive(AsRef)]
struct Types {
    #[as_ref(str)]
    str: String,
    #[as_ref([u8])]
    vec: Vec<u8>,
}

let item = Types {
    str: "test".to_owned(),
    vec: vec![0u8],
};

let _: &str = item.as_ref();
let _: &[u8] = item.as_ref();

Enums

Deriving AsRef for enums is not supported.