Since when have a mutable reference been available in `impl Trait` as an argument type?

This may be my misunderstanding, previously I think the following code could not be compiled.

fn decode(self, buf: &mut impl AsMut<[u8]>) {
    ...
}

However, when I tried to compile the code like above in Rust 1.81.0, it compiled successfully.

Since when have impl Trait as an argument type been available as a mutable reference like &mut impl AsMut<[u8]>? Or has this been available since impl Trait was available?

Yes, using impl Trait in argument position behind a (mutable) reference has worked since 1.26.0, when impl Trait was first stabilised.

3 Likes

When I want the following to be a generic function:

fn decode(self, buf: &mut [u8])

The valid declarations:

fn decode(self, buf: &mut (impl AsMut<[u8]> + ?Sized))
// or
fn decode<T: AsMut<[u8]> + ?Sized>(self, buf: &mut T)

And the invalid declaration:

fn decode(self, mut buf: (impl AsMut<[u8]> + ?Sized))

Is this the correct understanding?

Yes, the last one being invalid because arguments like buf can't be ?Sized.

1 Like
// or
fn decode<T: AsMut<[u8]> + ?Sized>(self, buf: T)

Argument position impl Trait and generics are almost[1] the same thing.


  1. APITs can't be named or used with precise return position impl Trait; the only benefit they have is not requiring <> brackets ↩︎

2 Likes

and they can't be ?Sized, because buf is passed by value, and without a known size, the callee has no way of knowing how large argument it's receiving. Fat pointers contain the length, and in Rust if there's no pointer, there's no length.

It’s true that everywhere a dynamically-sized value exists in Rust today, there is a pointer type involved, but that is not a necessary property.

  • Today, the calling convention on a given platform often specifies that large values are passed as implicitly-created pointers. This implicit pointer could be accompanied by the metadata[1] without any language-level pointer existing.
  • Even without any pointers, there is no semantic reason Rust can’t use a (metadata, data...) representation if desired.

The compiler even already has unstable/internal support for this under feature(unsized_fn_params) — which is flawed as currently implemented, but demonstrates that it can be done at least in some cases; it just isn't stable and complete enough to offer to users.


  1. Metadata is either a slice length or a vtable pointer containing a size. ↩︎

1 Like

As you pointed out, ?Sized cannot be added.

The valid code is probably:

fn decode(self, mut buf: impl AsMut<[u8]>)

Either way, this should be different from buf: &mut (impl AsMut<[u8]> + ?Sized).

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.