Attribute Macro borrowme::borrowme

source ·
#[borrowme]
Expand description

Automatically build an owned variant of a type and implement ToOwned and Borrow.

Anything captured by the macro will be forwarded to the generated variant. To have detailed control over this behavior, see the #[borrowed_attr(<meta>)] and #[owned_attr(<meta>)] attributes below.

In order to work as intended, #[borrowme] must be used before any attributes that you want it to capture such as derives.

use serde::Serialize;

#[borrowme]
#[derive(Serialize)]
pub struct Word<'a> {
    lang: &'a str,
}

implements_serialize::<Word<'_>>();
implements_serialize::<OwnedWord>();

If we use the wrong order, Serialize won’t be seen and that derive won’t be transferred to OwnedWord.

use serde::Serialize;

#[derive(Serialize)]
#[borrowme]
pub struct Word<'a> {
    lang: &'a str,
}

implements_serialize::<Word<'_>>();
implements_serialize::<OwnedWord>();
15 | implements_serialize::<OwnedWord>();
   |                        ^^^^^^^^^ the trait `Serialize` is not implemented for `OwnedWord`

Implementing for asymmetric types

Some common types need special care because their ToOwned and Borrow implementations are asymmetric, that is that the Borrow::Target does not match the type that implements ToOwned. This is easily addressed by overriding the borrow implementation with #[borrowme(borrow_with = <path>)].

&[T]

The ToOwned implementation produces a Vec<T>, while Borrow of Vec<T> produces a Vec<&T::Target>. This can be fixed using Vec::as_slice.

use borrowme::borrowme;

#[borrowme]
struct VecField<'a> {
    #[borrowme(borrow_with = Vec::as_slice)]
    strings: &'a [String],
}

&[T] as an owned Box<[T]>

The ToOwned implementation produces a Vec<T>, while borrowing that produces a Vec<&T::Target>. This can be fixed using Box::from.

use borrowme::borrowme;

#[borrowme]
struct Bytes<'a> {
    #[borrowme(owned = Box<[u8]>, to_owned_with = Box::from)]
    bytes: &'a [u8],
}

Why isn’t this a derive?

A derive macro can’t see other attributes than the ones it declares as its own. While this is very useful to provide better encapsulation across macros it would mean that derives and other attributes that are specified on the borrowed variant can’t be forwarded to the owned one without explicitly doing it yourself. This would be tedious because most of the time it’s the behaviour you want.

This should hopefully illustrate the issue:

use borrowme::{ToOwned, Borrow};

#[derive(Debug, Clone, PartialEq, Eq, ToOwned)]
#[owned_attr(derive(Debug, Clone, PartialEq, Eq, Borrow))]
struct Word<'a> {
    text: &'a str,
}

Container attributes

This section documents supported container attributes:

Container attributes are attributes which are added to a container, such as a struct or an enum. See for example #[borrowme(name = WordBuf)] below:

#[borrowme(name = WordBuf)]
struct Word<'a> {
    /* body */
}

#[borrowme(name = EnumBuf)]
enum Enum<'a> {
    /* body */
}

#[borrowme(name = <ident>)] container attribute

This allows you to pick the name to use for the generated type. By default this is the original identifier prefixed with Owned.

#[borrowme(name = WordBuf)]
#[derive(Debug, PartialEq)]
struct Word<'a> {
    text: &'a str,
}

let word = Word {
    text: "Hello World",
};

let word2 = WordBuf {
    text: String::from("Hello World"),
};

assert_eq!(borrowme::to_owned(&word), word2);

#[borrowed_attr(<meta>)] container attribute

Apply the given <meta> as a container attribute, but only for the borrowed variant.

#[borrowme]
#[borrowed_attr(derive(Clone))]
struct Word<'a> {
    text: &'a str,
}

let word = Word {
    text: "Hello World"
};

let word2 = word.clone();
assert_eq!(word.text, word2.text);

#[owned_attr(<meta>)] container attribute

Apply the given given <meta> as a container attribute, but only to the owned variant.

#[borrowme]
#[owned_attr(derive(Clone))]
struct Word<'a> {
    text: &'a str,
}

let word = OwnedWord {
    text: String::from("Hello World")
};

let word2 = word.clone();
assert_eq!(word.text, word2.text);

Variant attributes

This section documents supported variant attributes:

Variant attributes are attributes which apply to enum variants.


#[borrowed_attr(<meta>)] variant attribute

Apply the given <meta> as a variant attribute, but only for the borrowed variant.

#[borrowme]
#[borrowed_attr(derive(Default))]
enum Word<'a> {
    Wiktionary(&'a str),
    #[borrowed_attr(default)]
    Unknown,
}

let word = Word::default();
assert!(matches!(word, Word::Unknown));

#[owned_attr(<meta>)] variant attribute

Apply the given <meta> as a variant attribute, but only for the owned variant.

#[borrowme]
#[owned_attr(derive(Default))]
enum Word<'a> {
    Wiktionary(&'a str),
    #[owned_attr(default)]
    Unknown,
}

let word = OwnedWord::default();
assert!(matches!(word, OwnedWord::Unknown));

Field attributes

This section documents supported field attributes:

Field attributes are attributes which apply to fields, such as the fields in a struct.


#[owned(<type>)] or #[borrowme(owned = <type>)] field attributes

This specifies the owned type of the field. The latter variation is available so that it looks better when combined with other attributes.

By default we try to automatically figure out the type through ToOwned::Owned by converting the field type into an expression such as <<&'static str> as ::borrowme::ToOwned>::Owned. When this doesn’t work as expected like when using a type which does not implement ToOwned this can be overriden using this attribute.

struct MyType;
struct MyOwnedType;

#[borrowme]
pub struct Word<'a> {
    #[borrowme(owned = MyOwnedType, to_owned_with = to_owned_my_type, borrow_with = borrow_my_type)]
    lang: &'a MyType,
}

fn to_owned_my_type(this: &MyType) -> MyOwnedType {
    MyOwnedType
}

fn borrow_my_type(this: &MyOwnedType) -> &MyType {
    &MyType
}

#[borrowme(mut)] field attribute

Indicates that the field required mutable access to the parent container.

By default this uses heuristics. If a &mut T reference is noticed in the field type mutable access is assumed.

#[borrowme]
pub struct Text<'a> {
    text: &'a mut String,
}

#[borrowme]
pub struct Word<'a> {
    #[borrowme(mut)]
    text: Text<'a>,
}

#[borrowme(to_owned_with = <path>)] field attribute

Specifies a path to use when making a field owned. By default this is:

  • Clone if #[borrowme(std)] is specified.
  • An owned self.<field> expression if #[copy] is specified.
  • ::borrowme::ToOwned::to_owned.
#[borrowme]
#[derive(Clone, Debug)]
pub struct Word<'a> {
    #[borrowme(owned = Option<String>, to_owned_with = option_to_owned)]
    lang: Option<&'a str>,
}

#[inline]
pub(crate) fn option_to_owned(option: &Option<&str>) -> Option<String> {
    option.map(ToOwned::to_owned)
}

#[borrowme(borrow_with = <path>)] field attribute

Specifies a path to use when borrowing a field. By default this is:

  • A borrowed &self.<field> if #[borrowme(std)] is specified and the field is not mutable.
  • An owned self.<field> expression if #[copy] is specified.
  • ::borrowme::Borrow::borrow.
#[borrowme]
pub struct Word<'a> {
    #[borrowme(owned = Option<String>, borrow_with = option_borrow)]
    lang: Option<&'a str>,
    // Note that the above works the same as `Option::as_deref`.
    #[borrowme(owned = Option<String>, borrow_with = Option::as_deref)]
    lang2: Option<&'a str>,
}

#[inline]
pub(crate) fn option_borrow(option: &Option<String>) -> Option<&str> {
    option.as_deref()
}

#[borrowme(borrow_mut_with = <path>)] field attribute

Using this implies #[borrowme(mut)].

Specifies a path to use when borrowing a mutable field. By default this is:

  • A borrowed &mut self.<field> if #[borrowme(std)] is specified and a mutable field is detected or specified with #[borrowme(mut)].
  • An owned self.<field> expression if #[copy] is specified.
  • ::borrowme::BorrowMut::borrow_mut.
#[borrowme]
pub struct Word<'a> {
    #[borrowme(owned = Option<String>, borrow_mut_with = option_borrow)]
    lang: Option<&'a mut str>,
    #[borrowme(owned = Option<String>, borrow_mut_with = Option::as_deref_mut)]
    lang2: Option<&'a mut str>,
}

#[inline]
pub(crate) fn option_borrow(option: &mut Option<String>) -> Option<&mut str> {
    option.as_deref_mut()
}

#[borrowme(with = <path>)] field attribute

Specifies a path to use when calling to_owned and borrow on a field.

The sets to_owned to <path>::to_owned, and borrow to <path>::borrow.

Unless #[copy] or #[borrowme(std)] are specified, these are by default:

  • ::borrowme::ToOwned::to_owned
  • ::borrowme::Borrow::borrow.
#[borrowme]
#[derive(Clone, Debug)]
pub struct Word<'a> {
    #[owned(String)]
    text: &'a str,
    #[borrowme(owned = Option<String>, with = self::option)]
    lang: Option<&'a str>,
}

pub(crate) mod option {
    use borrowme::{Borrow, ToOwned};

    #[inline]
    pub(crate) fn borrow<T>(this: &Option<T>) -> Option<T::Target<'_>>
    where
        T: Borrow,
    {
        match this {
            Some(some) => Some(some.borrow()),
            None => None,
        }
    }

    #[inline]
    pub(crate) fn to_owned<T>(option: &Option<T>) -> Option<T::Owned>
    where
        T: ToOwned,
    {
        option.as_ref().map(ToOwned::to_owned)
    }
}

#[copy] and #[no_copy] field attribute

These can also be specified as #[borrowme(copy)] and #[borrowme(no_copy)].

Indicates that the field type is Copy, if this is set then the value is not cloned when the type is converted to and from its owned variant.

#[derive(Clone, Copy)]
struct OwnedBool(bool);

#[borrowme]
pub struct Word<'a> {
    text: &'a str,
    #[copy]
    teineigo: OwnedBool,
}

By default the macro performs heuristic to determine if a field is Copy or not. This means that prelude types which make up common copy configurations will be treated as copy. If this happens inadvertently the #[no_copy] or #[borrowme(std)] attributes can be used.

Type which are #[copy] by default are:

  • u8, u16, u32, u64, u128, and usize.
  • i8, i16, i32, i64, i128, and isize.
  • f32 and f64.
  • bool.
  • Tuple types (A, B, ..) for which all of its elements look like they are copy.
  • Array types [T; N] for which the element T looks like they are copy.

This heuristic can be defeated in a handful of ways, depending on what best suits your needs.

A field can specify that the type is not Copy using #[no_copy], which makes it fall back to the default behavior:

struct bool;

impl borrowme::ToOwned for bool {
    type Owned = bool;

    fn to_owned(&self) -> Self::Owned {
        bool
    }
}

impl borrowme::Borrow for bool {
    type Target<'a> = &'a bool;

    fn borrow(&self) -> Self::Target<'_> {
        self
    }
}

#[borrowme]
pub struct Word<'a> {
    #[no_copy]
    teineigo: &'a bool,
}

The field can specify #[borrowme(std)] to make use of standard methods of cloning and getting a reference:

#[derive(Clone)]
struct bool;

#[borrowme]
pub struct NoCopy<'a> {
    #[borrowme(std)]
    teineigo: &'a bool,
}

Finally we can specify everything ourselves:

#[derive(Clone)]
struct bool;

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

#[borrowme]
pub struct Word<'a> {
    #[borrowme(owned = bool, borrow_with = AsRef::as_ref, to_owned_with = Clone::clone)]
    teineigo: &'a bool,
}

#[borrowme(std)] field attribute

Causes conversion to happen by using the Clone trait to convert into an owned type and a reference expression like &self.<field> to borrow, mimicking the behaviour of std::borrow.

If the field is an immediate type behind a reference, that will be used as the target unless #[borrowme(owned = <type>)] is specified.

#[derive(Clone)]
struct WordKind;

#[borrowme]
pub struct Word<'a> {
    #[borrowme(std)]
    kind: &'a WordKind,
}

#[borrowed_attr(<meta>)] field attribute

Apply the given <meta> as a field attribute, but only for the borrowed variant. This allows certain attributes that are only needed for the borrowed variant to be implemented, such as #[serde(borrow)].

use serde::{Serialize, Deserialize};

#[borrowme]
#[derive(Serialize, Deserialize)]
pub struct Word<'a> {
    #[borrowed_attr(serde(borrow))]
    lang: Option<&'a str>,
}

#[owned_attr(<meta>)] field attribute

Apply the given <meta> as a field attribute, but only for the owned variant.

In the below example, only the owned variant will have a serde implementation:

use serde::{Serialize, Deserialize};

#[borrowme]
#[owned_attr(derive(Serialize, Deserialize))]
struct Word<'a> {
    #[owned_attr(serde(default, skip_serializing_if = "Option::is_none"))]
    lang: Option<&'a str>,
}