Use of the Type alias

I guess what I really want is a good explanation of how the type keyword/command should be used. My first impression is that it's main use is for clarifying code. For instance,

type Words = String;

means that I can use Words in my code as a substitute for declaring String. If I'm right about this, then this line should be valid:

let talk: Words = Words::from("Here are some words.");

Honestly, that doesn't look correct to me. Could someone explain to me the use of Type aliasing? Thanks, and then I have some questions about how I've been seeing it used in examples I pulled off of github.

This code compiles, so it is valid. It's easy to test.

fn main() {
    type Words = String;
    let talk: Words = Words::from("Here are some words.");
}

From my experience, type aliases are mostly used when you have a complex type, that is unwieldy to type out every time, like here:

A related but different use of the keyword is for associated types Associated types - Rust By Example

1 Like

Type aliases are not really useful for single-word concrete types. If you want Word to be a separate thing from a raw String, then you should not make it identical to String, you should define a newtype instead.

Type aliases are primarily useful for simplifying complicated generic types. For example, in my database abstraction library, I once needed to express the act of compiling queries from a general query DSL to a type-erased, backend-specific representation ("prepared statement"), for which I needed something along the lines of:

type CompiledQuery<A, R, E> = Box<
    dyn Query<
        <A as ToSql>::Borrowed,
        <R as FromSql>::Owned,
        QueryError<E>,
    >
>;

Obviously, writing CompiledQuery<Args, Ret, Err> is a lot clearer and less painful than having to spell out the whole dyn Query<...> part every single time.

4 Likes

Oh. So it actually worked. I didn't think it was even worth trying to compile. Thanks. And, yes, it makes sense to use it in more complex ways than my quick example. What prompted this question is the way I have seen it used in the examples provided by the Iced GUI github. Here are some variations:

type Message = Message;  // Where Message has already been defined as an enum.  Seems redundant to me.

type Flags = ();  // This was defined and used exactly once.  Again, it seems unnecessary to me.

I've seen other variations. Maybe someone could fill me in on how the redundancy of the first example can be useful. I'd also like to know how defining an alias for the unit () would ever be helpful. It must be, somehow, but I don't see it.

Here is the URL to an Iced example that uses both of those aliases: https://github.com/iced-rs/iced/tree/master/examples/checkbox.

Those are inside an impl block, where they instead are associated types

4 Likes

As I understand it they are just a convenience, almost like a macro. One thing I noticed recently is cargo doc sometimes expands them. For example at

There is

type Item = (Rc<RefCell<Page>>, usize)

However in the source code I wrote

impl Iterator for Asc {
    type Item = (PagePtr, usize);

where PagePtr is an alias for Rc<RefCell<Page>>

Which slightly confused me.

In cases where you are really defining a type alias and not an associated type, giving names is mainly about clarity and expressing intent.

Programming languages don't only contain features that are strictly necessary for expressing raw computation. If it weren't for making code more readable to humans, we would all be writing assembler or Brainfuck.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.