在Rust中调用指定为泛型的结构体的方法

huangapple go评论74阅读模式
英文:

Calling a method of a struct that was specified as generic, in Rust

问题

以下是您要翻译的内容:

"我是Rust的新手,正在尝试使用泛型。受到C++中相同方法的启发,下面的代码无法编译通过。我已经多次阅读了有关Rust泛型的文章,但找不到相关的示例。

代码:

struct A<T> {
    t: T
}

impl<T> A<T> {
    fn f(&self) {
        self.t.g();
    }
}

struct MyType;

impl MyType {
    fn g(&self) {
        println!("bingo");
    }
}

fn main() {
    let mut a: A<MyType> = A {
        t: MyType
    };

    a.f();
}

这个想法很简单:具体类MyType确实有方法g。但是这里出了些问题...

5 | impl<T> A<T> {
  |      - 无法为此类型参数找到方法`g`
6 |     fn f(&self) {
7 |         self.t.g();
  |                ^ 在`T`中找不到方法

我漏掉了什么关键的东西吗?谢谢。"

英文:

I'm new to Rust and I'm playing with the generics. The following code, inspired by the same approach in C++, does not compile. I've read about Rust generics multiple times in many places, but could not find relevant examples.

The code:

struct A&lt;T&gt; {
	t: T
}

impl&lt;T&gt; A&lt;T&gt; {
	fn f(&amp;self) {
		self.t.g();
	}
}

struct MyType;

impl MyType {
	fn g(&amp;self) {
		println!(&quot;bingo&quot;);
	}
}

fn main() {
	let mut a: A&lt;MyType&gt; = A {
		t: MyType
	};

	a.f();
}

The idea is simple: concrete class MyType does have method g. But something is wrong here...

5 | impl&lt;T&gt; A&lt;T&gt; {
  |      - method `g` not found for this type parameter
6 |     fn f(&amp;self) {
7 |         self.t.g();
  |                ^ method not found in `T`

What is a critical thing I'm missing? Thanks.

答案1

得分: 6

尽管表面上看起来,Rust 中的泛型与C++中的模板有相似之处,但它们实际上有很大的不同。更有帮助的方式是将 A<T> 视为一个"函数",它对于任何具体类型 T,都会给你一个具体类型 A<T>。最重要的是,就像函数参数受到类型约束,并且可用操作的集合受到其类型的限制一样,类型泛型也受到特性实现的约束。

在这种情况下,对于类型 T 没有约束(除了一些隐式约束,你必须选择退出,比如 T 的大小在编译时已知),这也意味着你不能对 T 做任何特定的事情:你的核心必须与任何类型 T 一起工作。如果你希望类型 T 的值具有方法 g,那么你必须添加该约束。在Rust中,这些约束通过特性来表示。

trait HasMethodG {
  fn g(&self);
}

struct A<T> {
    t: T
}

impl<T: HasMethodG> A<T> {
    fn f(&self) {
        self.t.g();
    }
}

还要注意,仅仅使一种类型与某个特性"兼容"是不够的(如在其他语言中可能会出现)。在Rust中,你必须显式地告诉它你要让 MyType 实现 HasMethodG。这意味着以下的不能编译

struct MyType;

impl MyType {
    fn g(&self) {
        println!("bingo");
    }
}

但这个可以:

struct MyType;

impl HasMethodG for MyType {
    fn g(&self) {
        println!("bingo");
    }
}
英文:

Despite the apparent similarity, generics in Rust are quite different from templates in C++. It's more helpful to think of A&lt;T&gt; as a "function" that, given any concrete type T, will give you a concrete type A&lt;T&gt;. Most importantly, just like function arguments are constraint by types and the set of available operations you can do on them is bound to their type, type generics are constraint by trait implementations.

In this case, there is no constraint over type T (besides some implicit constraints that you have to opt-out, such as the size of T is known at compile time), which also means that you can't do anything specific with T: your core has to work with any type T. If you want values of type T to have a method g, then you must add that constraint. In Rust, these constraints are expressed with traits.

trait HasMethodG {
  fn g(&amp;self);
}

struct A&lt;T&gt; {
    t: T
}

impl&lt;T: HasMethodG&gt; A&lt;T&gt; {
    fn f(&amp;self) {
        self.t.g();
    }
}

Also note that, it's not enough for a type to "be compatible with" a certain trait (as it may be in other languages). In Rust, you have to explicitly tell that you want MyType to implement HasMethodG. This means that the following doesn't compile

struct MyType;

impl MyType {
    fn g(&amp;self) {
        println!(&quot;bingo&quot;);
    }
}

but this does

struct MyType;

impl HasMethodG for MyType {
    fn g(&amp;self) {
        println!(&quot;bingo&quot;);
    }
}

答案2

得分: 1

您的方法仅在TMyType时才有意义,因此您应该在非泛型的impl块中编写它。

impl A<MyType> {
    fn f(&self) {
        self.t.g();
    }
}

如果您有多个具有g方法的类型,希望使其工作,您需要将g定义为一个特性方法,并将泛型绑定到该特性上。

trait G {
    fn g(&self);
}

impl G for MyType {
    fn g(&self) {
        println!("bingo");
    }
}

impl<T: G> A<T> {
    fn f(&self) {
        self.t.g();
    }
}
英文:

Your method only makes sense when T is MyType, so you should write it in a non-generic impl block.

impl A&lt;MyType&gt; {
    fn f(&amp;self) {
        self.t.g();
    }
}

If you have multiple types with a g method that you want this to work with, you need to make g a trait method and bind the generic to a trait.

trait G {
    fn g(&amp;self);
}

impl G for MyType {
    fn g(&amp;self) {
        println!(&quot;bingo&quot;);
    }
}

impl&lt;T: G&gt; A&lt;T&gt; {
    fn f(&amp;self) {
        self.t.g();
    }
}

huangapple
  • 本文由 发表于 2023年6月16日 02:44:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76484641.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定