简体   繁体   中英

What is the difference between generics and associated types?

I was under the impression that these two things were only semantically different.

However, this is possible:

struct Foo;

trait Bar<T> {
  fn resolve(&self) -> T;
}

impl Bar<isize> for Foo {
  fn resolve(&self) -> isize {
    return 0isize;
  }
}

impl Bar<usize> for Foo {
  fn resolve(&self) -> usize {
    return 1usize;
  }
}

#[test]
fn test_foo() {
  let foo = Foo;
  assert!((&foo as &Bar<isize>).resolve() == 0isize);
  assert!((&foo as &Bar<usize>).resolve() == 1usize);
}

While this is not:

struct Foo;

trait Bar {
  type T;
  fn resolve(&self) -> Self::T;
}

impl Bar for Foo {
  type T = isize;
  fn resolve(&self) -> isize {
    return 0isize;
  }
}

impl Bar for Foo {
  type T = usize;
  fn resolve(&self) -> usize {
    return 1usize;
  }
}

#[test]
fn test_foo() {
  let foo = Foo;
  assert!((&foo as &Bar<T = isize>).resolve() == 0isize);
  assert!((&foo as &Bar<T = usize>).resolve() == 1isize);
}

It generates:

<anon>:8:1: 13:2 error: conflicting implementations for trait `Bar` [E0119]
<anon>: 8 impl Bar for Foo {
<anon>: 9   type T = isize;
<anon>:10   fn resolve(&self) -> isize {
<anon>:11     return 0isize;
<anon>:12   }
<anon>:13 }

Am I missing something?

Is there a special syntax for what I'm trying to achieve, or is there really a... technical... distinction between a generic and an associated type?

Is there some circumstance in which an associated type has a tangible (rather than purely code prettiness) benefit over using a generic?

I'll repeat my comment: it is true that type parameters and associated types are only semantically different. However, that's the main point why they are both present in the language - they do their own separate jobs, so it is not "just" semantic difference, it is the whole reason for their existence as a separate thing from type parameters.

Note that I do not even touch syntactic differences. Of course it is absolutely natural that there are syntactic differences. These are separate features after all; if they had no syntactic differences, then how you would distinguish between them? Their syntactic difference is closely tied to the semantic difference, because the way associated types are defined makes it clear that they have "output" position, compared to "input" position of type parameters, but technically both type parameters and associated types (and also the implicit Self , parameter, by the way) are the same thing.

For anyone else who finds this question, there is also another technical distinction between type parameters and associated types as well.

If you attempt to implement a trait with an associated type you may see the error:

src/lib.rs:10:1: 15:2 error: the impl does not reference any types
defined in this crate; only traits defined in the current crate can
be implemented for arbitrary types [E0117]

If you have traits exported in a crate bar:

pub trait BarParam<TRtn> {
  fn bar() -> TRtn;
}

pub trait BarAssoc {
  type TRtn;
  fn bar() -> Self::TRtn;
}

You will find that a crate importing these traits will only be able to implement:

impl<'a> BarParam<Foo> for &'a str {
  fn bar() -> Foo {
    return Foo;
  }
}

While attempting to implement:

impl<'a> BarAssoc for &'a str {
  type TRtn = Foo;
  fn bar() -> Foo {
    return Foo;
  }
}

Will result in the error above.

Frankly I'm not really sure what's going on here, so if you do, by all means add a comment or another answer; but this is a tangible reason to avoid associated types when you're writing a crate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM