Jim
Jim

Reputation: 226

What is the right way to be generic over anything that looks like a slice?

I have a struct that involves some data stored in a slice. I would like to let the user decide whether that slice is a reference or owned, stored on the stack or the heap, etc, all I care about is that it's some sort of slice somewhere.

pub struct A<T: WhatGoesHere?>(pub T);

A(vec![1, 2, 3]);  // Mutable on heap
A([1, 2, 3]);      // Mutable on stack
A(&[1, 2, 3]);     // Immutable

At first I tried the deref trait, T: Deref<Target=[u32]>, and for impl's that require mutability I would bound by DerefMut. That works for the vec, but it doesn't like the other two.

Then I read about the Borrow trait, T: Borrow<[u32]>. Seemed like a good fit and I could bound on BorrowMut for mutability. This one likes the vec and the array, but the slice can't be borrowed as an array. I thought maybe I should be using Borrow<&[u32]> but I can't figure out the lifetimes.

Finally, I tried T: AsRef<[u32]>. This works for all 3! But for mutability AsMut doesn't require AsRef (why?) so I have to bound on AsRef<[u32]> + AsMut<[u32]>. That feels weird and I have to trust that those two references are to the same slice and the user didn't do something weird. It doesn't quite feel like it's the right thing to do.

What's the right bound for T? If AsRef is indeed the right thing should I be worried about AsMut referencing a different slice or am I being too paranoid?

Upvotes: 2

Views: 565

Answers (1)

Greaka
Greaka

Reputation: 755

To answer your question directly: AsRef and AsMut are the reasonable choice and you don't even need to bind to both for the case of AsMut.

However, depending on what you want to achieve with this slice, you might even want to consider using Read, Write, or their async equivalents (AsyncRead, AsyncWrite).

Upvotes: 1

Related Questions