Skip to content

Latest commit

 

History

History
232 lines (190 loc) · 7.84 KB

implementations.md

File metadata and controls

232 lines (190 loc) · 7.84 KB

Implementations

Syntax
Implementation :
   InherentImpl | TraitImpl

InherentImpl :
   impl Generics? Type WhereClause? {
      InnerAttribute*
      InherentImplItem*
   }

InherentImplItem :
   OuterAttribute* (
         MacroInvocationSemi
      | ( Visibility? ( ConstantItem | Function | Method ) )
   )

TraitImpl :
   unsafe? impl Generics? !? TypePath for Type
   WhereClause?
   {
      InnerAttribute*
      TraitImplItem*
   }

TraitImplItem :
   OuterAttribute* (
         MacroInvocationSemi
      | ( Visibility? ( TypeAlias | ConstantItem | Function | Method ) )
   )

An implementation is an item that associates items with an implementing type. Implementations are defined with the keyword impl and contain functions that belong to an instance of the type that is being implemented or to the type statically.

There are two types of implementations:

  • inherent implementations
  • trait implementations

Inherent Implementations

An inherent implementation is defined as the sequence of the impl keyword, generic type declarations, a path to a nominal type, a where clause, and a bracketed set of associable items.

The nominal type is called the implementing type and the associable items are the associated items to the implementing type.

Inherent implementations associate the contained items to the implementing type. Inherent implementations can contain associated functions (including methods) and associated constants. They cannot contain associated type aliases.

The path to an associated item is any path to the implementing type, followed by the associated item's identifier as the final path component.

A type can also have multiple inherent implementations. An implementing type must be defined within the same crate as the original type definition.

pub mod color {
    pub struct Color(pub u8, pub u8, pub u8);

    impl Color {
        pub const WHITE: Color = Color(255, 255, 255);
    }
}

mod values {
    use super::color::Color;
    impl Color {
        pub fn red() -> Color {
            Color(255, 0, 0)
        }
    }
}

pub use self::color::Color;
fn main() {
    // Actual path to the implementing type and impl in the same module.
    color::Color::WHITE;

    // Impl blocks in different modules are still accessed through a path to the type.
    color::Color::red();

    // Re-exported paths to the implementing type also work.
    Color::red();

    // Does not work, because use in `values` is not pub.
    // values::Color::red();
}

Trait Implementations

A trait implementation is defined like an inherent implementation except that the optional generic type declarations is followed by a trait followed by the keyword for. Followed by a path to a nominal type.

The trait is known as the implemented trait. The implementing type implements the implemented trait.

A trait implementation must define all non-default associated items declared by the implemented trait, may redefine default associated items defined by the implemented trait, and cannot define any other items.

The path to the associated items is < followed by a path to the implementing type followed by as followed by a path to the trait followed by > as a path component followed by the associated item's path component.

Unsafe traits require the trait implementation to begin with the unsafe keyword.

# #[derive(Copy, Clone)]
# struct Point {x: f64, y: f64};
# type Surface = i32;
# struct BoundingBox {x: f64, y: f64, width: f64, height: f64};
# trait Shape { fn draw(&self, Surface); fn bounding_box(&self) -> BoundingBox; }
# fn do_draw_circle(s: Surface, c: Circle) { }
struct Circle {
    radius: f64,
    center: Point,
}

impl Copy for Circle {}

impl Clone for Circle {
    fn clone(&self) -> Circle { *self }
}

impl Shape for Circle {
    fn draw(&self, s: Surface) { do_draw_circle(s, *self); }
    fn bounding_box(&self) -> BoundingBox {
        let r = self.radius;
        BoundingBox {
            x: self.center.x - r,
            y: self.center.y - r,
            width: 2.0 * r,
            height: 2.0 * r,
        }
    }
}

Trait Implementation Coherence

A trait implementation is considered incoherent if either the orphan rules check fails or there are overlapping implementation instances.

Two trait implementations overlap when there is a non-empty intersection of the traits the implementation is for, the implementations can be instantiated with the same type.

Orphan rules

Given impl<P1..=Pn> Trait<T1..=Tn> for T0, an impl is valid only if at least one of the following is true:

  • Trait is a local trait
  • All of
    • At least one of the types T0..=Tn must be a local type. Let Ti be the first such type.
    • No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti)

Only the appearance of uncovered type parameters is restricted. Note that for the purposes of coherence, fundamental types are special. The T in Box<T> is not considered covered, and Box<LocalType> is considered local.

Generic Implementations

An implementation can take type and lifetime parameters, which can be used in the rest of the implementation. Type parameters declared for an implementation must be used at least once in either the trait or the implementing type of an implementation. Implementation parameters are written directly after the impl keyword.

# trait Seq<T> { fn dummy(&self, _: T) { } }
impl<T> Seq<T> for Vec<T> {
    /* ... */
}
impl Seq<bool> for u32 {
    /* Treat the integer as a sequence of bits */
}

Attributes on Implementations

Implementations may contain outer attributes before the impl keyword and inner attributes inside the brackets that contain the associated items. Inner attributes must come before any associated items. That attributes that have meaning here are cfg, deprecated, doc, and the lint check attributes.