如何最好地提示rustc,基于Pointee::Metadata的递归数据结构是良好形成的?

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

How to best hint at rustc that a recursive data-structure based on Pointee::Metadata is well-formed?

问题

The Rust compiler is encountering an issue with type inference and resolution in the provided code. The error suggests that type annotations are needed, and it points to a situation where it cannot infer the type for next in the TypedHandle struct.

This might be related to Rust's restrictions on dynamically sized types in structs. The error message provides some possible solutions, like using NonNull<E> instead of &lt;E as Pointee&gt;::Metadata. However, these solutions might impact the code's functionality.

You mentioned that adding a where NodeHandle<T, H>: Sized bound to Node resolves the issue but is unwieldy. It's worth noting that Rust's handling of dynamically sized types in structs can sometimes lead to complex situations.

To answer your specific questions:

  1. This might not be a known limitation of the compiler but rather a result of how Rust handles dynamically sized types.

  2. A better way to solve the problem could depend on the specific requirements of your code. You might want to consider whether the use of dynamically sized types in your design is necessary or if there are alternative approaches that maintain the desired functionality while avoiding the type inference issues.

If you need further assistance or guidance on resolving this issue, feel free to provide more context or ask specific questions.

英文:

The Rust compiler chokes on the following code:

#![feature(ptr_metadata)]

use core::ptr::Pointee;

struct TypedHandle&lt;E: ?Sized, H&gt; {
    handle: H,
    metadata: &lt;E as Pointee&gt;::Metadata,
}

type NodeHandle&lt;T, H&gt; = TypedHandle&lt;Node&lt;T, H&gt;, H&gt;;

struct Node&lt;T, H&gt; {
    element: T,
    next: NodeHandle&lt;T, H&gt;,
    prev: NodeHandle&lt;T, H&gt;,
}

There seems to be a type inference/type resolution issue, however the notes are all over the place with weirdly seemingly unrelated suggestions:

error[E0284]: type annotations needed
  --&gt; src/lib.rs:14:11
   |
14 |     next: NodeHandle&lt;T, H&gt;,
   |           ^^^^^^^^^^^^^^^^ cannot infer type
   |
   = note: cannot satisfy `&lt;Node&lt;T, H&gt; as Pointee&gt;::Metadata == _`
note: required because it appears within the type `TypedHandle&lt;Node&lt;T, H&gt;, H&gt;`
  --&gt; src/lib.rs:5:8
   |
5  | struct TypedHandle&lt;E: ?Sized, H&gt; {
   |        ^^^^^^^^^^^
   = note: only the last field of a struct may have a dynamically sized type
   = help: change the field&#39;s type to have a statically known size
help: borrowed types always have a statically known size
   |
14 |     next: &amp;NodeHandle&lt;T, H&gt;,
   |           +
help: the `Box` type always has a statically known size and allocates its contents in the heap
   |
14 |     next: Box&lt;NodeHandle&lt;T, H&gt;&gt;,
   |           ++++                +

Various changes to the code "solve" the error (but break functionality):

  • Using NonNull&lt;E&gt; instead of &lt;E as Pointee&gt;::Metadata. This suggests that the recursive nature of the type is not the sole issue.
  • Removing the ?Sized bound on E in TypedHandle.
  • Removing one of next or prev. This suggests that the note about next not being able to be both unsized and non-last field is actually "serious", despite the fact that TypedHandle is always sized.

Various changes to the code that do not help:

  • Switching NodeHandle from type alias to tuple struct. It doesn't help with the error message either.

The only solution I found so far is adding where NodeHandle&lt;T, H&gt;: Sized bound to Node, which is quite unwieldy as this bound has to be replicated everywhere afterwards.

  • Is this a known limitation of the compiler?
  • Is there a better way to solve the problem?

Note: the problem appeared in a custom implementation of LinkedList as I explore a new allocation API, I worked around it by copy/pasting TypedHandle into SizedHandle and removing the ?Sized bound on E, which leaves an ugly taste in my mouth, but is easier to fix back than copy/pasted bounds all over the place.

答案1

得分: 1

这是一个每晚的回归问题,由 @lukas-code 在 此 GitHub 评论 中识别的,关于我提出的问题。

在同一评论中,他还建议了一个更轻量级的解决方法:添加一个 _tail: () 字段,以使编译器认识到类型是有大小的。

特别是,TypedHandle 的定义可以更改为:

struct TypedHandle<E: ?Sized, H> {
    handle: H,
    metadata: <E as Pointee>::Metadata,
    _self_is_always_sized: (),
}

以便解决方法尽可能有限制。

英文:

This is a nightly regression, which @lukas-code identified in this github comment on the I issue raised.

In the very same comment, he also suggests a much more lightweight work-around: adding a _tail: () field to make the compiler realize a type is sized.

In particular, the TypedHandle definition can be changed to:

struct TypedHandle&lt;E: ?Sized, H&gt; {
    handle: H,
    metadata: &lt;E as Pointee&gt;::Metadata,
    _self_is_always_sized: (),
}

So that the work-around is as scoped as possible.

huangapple
  • 本文由 发表于 2023年5月21日 22:45:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76300458.html
匿名

发表评论

匿名网友

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

确定