Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a "nominal type" in the context of an inherent implementation?

Tags:

rust

I am currently going through the Rust Documentation to understand inherent implementations. What is the "nominal type" and what are they referring to when then they say "associable items to the implementing type"?

Is there a related analog to this in C or C++?

like image 949
Roo Avatar asked Jan 03 '23 16:01

Roo


2 Answers

Well, that's the language reference. Learning Rust with that is certainly possible, but a little bit like trying to learn English by reading a dictionary. Have you tried the Rust Book?

Anyway, as the first paragraph states, the "nominal type" is, well:

impl /* --> */ Point /* <-- this is the "nominal type" */ {
    fn log(&self) { ... }
}

It's the type which is the subject of the inherent impl. An "associable item" is an item (like a fn, const, or type) which is associated with the nominal type.

If you had the paragraph:

Let's talk about Raymond. His hair is brown. He knows how to dance.

That would be roughly equivalent to:

struct Raymond; // introduce the Raymond type.

impl Raymond { // associate some items with Raymond.
    const HAIR: Colour = Colour::Brown;

    fn dance(&mut self) { ... }
}

fn main() {
    let mut ray = Raymond;
    println!("Raymond's hair is {}", Raymond::HAIR);
    ray.dance();
}

(As an aside: the pronouns in the paragraph (like "he" or "him") would become self or Self in the impl.)

The impl is associating those items with the nominal type. Notice how HAIR is "inside" of the Raymond type. You could also write the above code as:

struct Raymond;

const RAYMOND_HAIR: Colour = Colour::Brown;

fn raymond_dance(ray: &mut Raymond) { ... }

fn main() {
    let mut ray = Raymond;
    println!("Raymond's hair is {}", RAYMOND_HAIR);
    raymond_dance(&mut ray);
}

Here, there're no inherent impls, so the RAYMOND_HAIR and raymond_dance items aren't associated with the Raymond type directly. There's no fundamental difference between the two, other than convenience.

As for tying this back to C++... that's tricky since Rust distinguishes between inherent and non-inherent impls and C++... doesn't. The closest analogue would be to say that they're like the parts of a struct body that aren't fields and aren't overriding methods in a base class.

like image 66
DK. Avatar answered Jan 05 '23 17:01

DK.


An inherent implementation is the equivalent of creating a class in a OOP language. The difference in Rust is that data is separated from implementation:

/* Data */
struct Foo {
    // all data there
    //...
}

/* Inherent implementation */
impl Foo {
    fn bar(&self) {
        //...
    }
}
  • The nominal type is the data that you implement.
  • The associable items are the methods that you add to the data. Those functions are special because you can call them with the syntax foo.bar().

The inherent implementation is called like that as opposed to trait implementation:

/* Trait implementation */
impl Debug for Foo {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        //...
    }
}
  • In the case of inherent implementation, the method is bound to the data only. It has a sense only with this data.
  • In the case of a trait implementation, the method can be implemented for any data that implements the trait.

The equivalent in C++ could be:

struct Debug {
    virtual std::string fmt() = 0;
}

class Foo: public Debug {
    // all data there
    //...

public:
    /* Equivalent of inherent implementation */
    void bar() {
        //...
    }

    /* Equivalent of trait implementation:
       implementation of base class */
    std::string fmt() {
        //...
    }
}

In C++, you cannot separate "inherent implementation" from "trait implementation" (I put those between quotes, because those terms do not make sense in C++, of course).


Note that unlike in C++, in Rust the methods are not really different that a free function. You can call the bar method like this:

Foo::bar(foo);

and if you define this function:

fn qux(f: &Foo) {
    //...
}

it will have the same signature as Foo::bar.

like image 41
Boiethios Avatar answered Jan 05 '23 18:01

Boiethios