How to modify my constructor in order to accept either a slice or a reference to array or vector - rust

This is a simplified example of my code:
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
impl<'a> From<&'a [i32]> for Data<'a> {
fn from(v: &'a [i32]) -> Data<'a> {
Data::I32(v)
}
}
impl<'a> From<&'a [f64]> for Data<'a> {
fn from(v: &'a [f64]) -> Data<'a> {
Data::F64(v)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: T) -> Self
where
T: Into<Data<'a>>,
{
Self {
name,
data: data.into(),
}
}
}
First of all, considering that I need to cast different DataVars to the same vector, and I would like to avoid using trait objects, do you think my implementation is correct or do you have suggestions for improvement?
Now my main question. I can define new DataVars passing a slice, for instance as follows:
let x = [1, 2, 3];
let xvar = DataVar::new("x", &x[..]);
How can I modify my constructor so that it works not only with a slice, but also with a reference to array or vector? For instance I would like the following to work as well:
let x = [1, 2, 3];
let xvar = DataVar::new("x", &x);
EDIT:
Now I tried implementing the same code using a trait object instead of an enum, but the result is even worse... isn't there really any solution to this?
trait Data: std::fmt::Debug {}
impl Data for &[i32] {}
impl Data for &[f64] {}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: &'a dyn Data,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a T) -> Self
where
T: Data,
{
Self { name, data }
}
}
let x = [1, 2, 3];
let xvar = DataVar::new("x", &&x[..]);

To me, AsRef doesn't seem to be the right abstraction for two reasons: first, because it's possible (if unlikely) for a type to implement both AsRef<[i32]> and AsRef<[f64]>, and it's not clear what should happen in that case; and second, because there's already a built-in language feature (coercion) that can turn Vec<T> or &[T; n] into &[T], and you're not taking advantage of it.
What I'd like is to write a new function that looks basically like this:
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
// what goes here?
This will automatically work with &[T; n], &Vec<T>, &Cow<T>, etc. if we can tell the compiler what to do with T. It makes sense that you could make a trait that knows how to convert &'a [Self] to Data and is implemented for i32 and f64, so let's do that:
trait Item: Sized {
fn into_data<'a>(v: &'a [Self]) -> Data<'a>;
}
impl Item for i32 {
fn into_data<'a>(v: &'a [i32]) -> Data<'a> {
Data::I32(v)
}
}
impl Item for f64 {
fn into_data<'a>(v: &'a [f64]) -> Data<'a> {
Data::F64(v)
}
}
The trait bound on new becomes trivial:
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
T: Item,
{
Self {
name,
data: T::into_data(data),
}
}
}
I find this more readable than the version with From and AsRef, but if you still want From, you can easily add it with a generic impl:
impl<'a, T> From<&'a [T]> for Data<'a>
where
T: Item,
{
fn from(v: &'a [T]) -> Self {
T::into_data(v)
}
}

We can use the AsRef trait to convert references to arrays or vectors to slices. AsRef is a generic trait, so we need to introduce a second type parameter to represent the "intermediate type" (the slice type). After calling as_ref, we've got a slice that can be converted to a Data using into.
impl<'a> DataVar<'a> {
fn new<T, U>(name: &'a str, data: &'a T) -> Self
where
T: AsRef<U> + ?Sized,
U: ?Sized + 'a,
&'a U: Into<Data<'a>>,
{
Self {
name,
data: data.as_ref().into(),
}
}
}
Note however that the data parameter is now a reference: this is necessary because the lifetime of the reference returned by as_ref is bound by the lifetime of the self parameter passed to as_ref. If we changed the parameter back to data: T, then data.as_ref() now implicitly references data in order to call as_ref, which expects a shared reference to self (&self). But data here is a local parameter, which means that the lifetime of the reference created by this implicit referencing operation is limited to the local function, and so is the reference returned by data.as_ref(). This lifetime is shorter than 'a, so we can't store it in the DataVar and return it.
If you need to handle data values that are not references in addition to values that are references, this solution cannot support that, unfortunately.

This is actually the best solution for my case:
impl<'a> DataVar<'a> {
fn new<T, U>(name: &'a str, data: &'a T) -> Self
where
T: AsRef<[U]> + ?Sized,
U: 'a,
&'a [U]: Into<Data<'a>>,
{
Self {
name,
data: data.as_ref().into(),
}
}
}
It works with slices, references to vectors, and references to arrays up to length 32 which implement AsRef<[T]> https://doc.rust-lang.org/beta/std/convert/trait.AsRef.html
Thanks #Francis for your hints!

Actually, this is IMHO the best solution... so similar to my initial code, I just needed a small fix in the new constructor:
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
impl<'a> From<&'a [i32]> for Data<'a> {
fn from(data: &'a [i32]) -> Data<'a> {
Data::I32(data)
}
}
impl<'a> From<&'a [f64]> for Data<'a> {
fn from(data: &'a [f64]) -> Data<'a> {
Data::F64(data)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
&'a [T]: Into<Data<'a>>,
{
Self {
name,
data: data.into(),
}
}
}

#trentcl your solution is brilliant! Now I see how to leverage coercion.
However I tweaked it a little bit as follows, I will finally use this code unless you see any drawbacks in it, thanks!
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
trait IntoData<'a>: Sized {
fn into_data(&self) -> Data<'a>;
}
impl<'a> IntoData<'a> for &'a [i32] {
fn into_data(&self) -> Data<'a> {
Data::I32(&self)
}
}
impl<'a> IntoData<'a> for &'a [f64] {
fn into_data(&self) -> Data<'a> {
Data::F64(&self)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
&'a [T]: IntoData<'a>,
{
Self {
name,
data: data.into_data(),
}
}
}

Related

Trait for conversion of references and owned values to Cow

I have a function that can handle both owned and borrowed values of some type Foo by accepting a Cow<'_, Foo>. However, I'd like to make it more convenient by allowing to pass in owned or borrowed Foos directly.
Is it possible to have a conversion trait to Cow that's implemented both on a reference type and its owned version?
This is what I tried:
trait Convert<'a> : Clone {
fn into_cow(self) -> Cow<'a, Self>;
}
// Works
impl<'a> Convert<'a> for Foo {
fn into_cow(self) -> Cow<'a, Self> {
println!("owned");
Cow::Owned(self)
}
}
// Doesn't work
impl<'a> Convert<'a> for &'a Foo {
fn into_cow(self) -> Cow<'a, Self> {
println!("borrowed");
Cow::Borrowed(self)
}
}
The borrowed version says that Borrowed expected a &&'a Foo but found a &'a Foo.
Self in the implementation of a trait for &Foo is &Foo, so into_cow() doesn't return the same type in both impls.
One solution would be to change the return type to Cow<'a, Foo>, but that of course limits the trait to only work on Foo.
A better way is to make the owned type a generic parameter of Convert like this:
trait Convert<'a, T: Clone> {
fn into_cow(self) -> Cow<'a, T>;
}
impl<'a, T: Clone> Convert<'a, T> for T {
fn into_cow(self) -> Cow<'a, T> {
println!("owned");
Cow::Owned(self)
}
}
impl<'a, T: Clone> Convert<'a, T> for &'a T {
fn into_cow(self) -> Cow<'a, T> {
println!("borrowed");
Cow::Borrowed(self)
}
}
fn take_foo<'a>(foo: impl Convert<'a, Foo>) {
let cow = foo.into_cow();
}
fn main() {
take_foo(&Foo{});
take_foo(Foo{});
}
Do you ever make use of the ownership, or always reborrow it? If only reborrowing, is std::convert::AsRef what you're looking for?
fn take_foo(foo: impl AsRef<Foo>) {
let foo: &Foo = foo.as_ref();
todo!();
}

Lifetimes for traits

Let there is some struct:
struct ResourceData { }
We can create its list:
pub struct ResourceList
{
pub v: Vec<ResourceData>,
}
but if I add a level of indirection:
pub trait Resource<'a> {
fn data(&self) -> &'a ResourceData;
fn data_mut(&mut self) -> &'a mut ResourceData;
}
then I am messed:
pub struct ResourceList2<R: Resource<'a>> // What should be 'a?
{
pub v: Vec<R>,
}
What should be 'a?
'a could be the lifetime of the vector, but if an element is removed, the lifetime of the references shorten, because references break. So, accordingly my understanding, 'a isn't the lifetime of the vector.
Resource should not have a lifetime argument, but instead just use elided lifetimes:
pub trait Resource {
fn position(&self) -> &ResourceData;
fn position_mut(&mut self) -> &mut ResourceData;
}
After understanding this, the question disappears, just:
pub struct ResourceList2<R: Resource> // What should be 'a?
{
pub v: Vec<R>,
}

Return a reference to a member iterator

I have a struct that has an iterator over a borrowed slice, and I want to create a method that returns an iterator that does some computation on the contained iterator
Basically this:
#[derive(Clone)]
struct Foo<'a> {
inner: ::std::iter::Cycle<::std::slice::Iter<'a, u8>>,
}
impl<'a> Foo<'a> {
pub fn new<T: AsRef<[u8]> + ?Sized>(vals: &'a T) -> Foo<'a> {
Self {
inner: vals.as_ref().iter().cycle(),
}
}
pub fn iter(&mut self) -> impl Iterator<Item = u8> + 'a {
self.inner.by_ref().map(Clone::clone) // simple case but this could be any computation
}
}
Which yields a cryptic error.
Here's my understanding: I need to bind the returned iterator's lifetime to the &mut self of iter() so that &mut self is only dropped when .collect() is called on the returned iterator.
BUT, changing to fn iter(&'a mut self) isn't good enough because &'a mut self now lives for the entire duration of the struct instance (meaning that calling collect() doesn't drop that reference). Which prevents something like this
#[test]
fn test(){
let mut foo = Foo::new("hello, there");
foo.iter().zip(0..4).collect::<Vec<_>>(); // call once, ok
foo.iter().zip(0..4).collect::<Vec<_>>(); // error because 2 &mut references exist at the same
}
rust playground link
The way I would do this is to make a struct just for iterating over those items. It makes things much easier.
...
pub fn iter(&mut self) -> ClonedFooIter<'a, '_> {
ClonedFooIter {
inner: &mut self.inner,
}
}
}
pub struct ClonedFooIter<'a, 'b> {
inner: &'b mut std::iter::Cycle<::std::slice::Iter<'a, u8>>,
}
impl<'a, 'b> Iterator for ClonedFooIter<'a, 'b> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().cloned()
}
}
The idea is that the returned struct only lives till the mutable borrow of self lives.
Using a struct makes naming the iterator type easier too.

How do I return the result of get_mut from a HashMap or a HashSet?

I'm trying to wrap a HashMap, as defined below, to return a mutable reference from a HashMap:
use std::{collections::HashMap, marker::PhantomData};
struct Id<T>(usize, PhantomData<T>);
pub struct IdCollection<T>(HashMap<Id<T>, T>);
impl<'a, T> std::ops::Index<Id<T>> for &'a mut IdCollection<T> {
type Output = &'a mut T;
fn index(&mut self, id: &'a Id<T>) -> Self::Output {
self.0.get_mut(id).unwrap()
}
}
And the resulting error:
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 54:5...
--> src/id_container.rs:54:5
|
54 | / fn index(&mut self, id: &'a Id<T>) -> Self::Output {
55 | | self.0.get_mut(id).unwrap()
56 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/id_container.rs:55:9
|
55 | self.0.get_mut(id).unwrap()
| ^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 52:6...
--> src/id_container.rs:52:6
|
52 | impl<'a, T> std::ops::Index<Id<T>> for &'a mut IdCollection<T> {
| ^^
= note: ...so that the types are compatible:
expected std::ops::Index<id_container::Id<T>>
found std::ops::Index<id_container::Id<T>>
Why can't the compiler extend the lifetime of the get_mut? The IdCollection would then be borrowed mutably.
Note that I tried using a std::collections::HashSet<IdWrapper<T>> instead of a HashMap:
struct IdWrapper<T> {
id: Id<T>,
t: T,
}
Implementing the proper borrow etc. so I can use the Id<T> as a key.
However, HashSet doesn't offer a mutable getter (which makes sense since you don't want to mutate what's used for your hash). However in my case only part of the object should be immutable. Casting a const type to a non-const is UB so this is out of the question.
Can I achieve what I want? Do I have to use some wrapper such as a Box? Although I'd rather avoid any indirection...
EDIT
Ok I'm an idiot. First I missed the IndexMut instead of the Index, and I forgot the & when specifying the Self::Output in the signature.
Here's my full code below:
pub struct Id<T>(usize, PhantomData<T>);
impl<T> std::fmt::Display for Id<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<T> Hash for Id<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T> PartialEq for Id<T> {
fn eq(&self, o: &Self) -> bool {
self.0 == o.0
}
}
impl<T> Eq for Id<T> {}
pub struct IdCollection<T>(HashMap<Id<T>, T>);
impl<'a, T> IntoIterator for &'a IdCollection<T> {
type Item = (&'a Id<T>, &'a T);
type IntoIter = std::collections::hash_map::Iter<'a, Id<T>, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a, T> IntoIterator for &'a mut IdCollection<T> {
type Item = (&'a Id<T>, &'a mut T);
type IntoIter = std::collections::hash_map::IterMut<'a, Id<T>, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
impl<T> std::ops::Index<Id<T>> for IdCollection<T> {
type Output = T;
fn index(&self, id: Id<T>) -> &Self::Output {
self.0.get(&id).unwrap()
}
}
impl<T> std::ops::IndexMut<Id<T>> for IdCollection<T> {
fn index_mut(&mut self, id: Id<T>) -> &mut Self::Output {
self.0.get_mut(&id).unwrap()
}
}
impl<T> std::ops::Index<&Id<T>> for IdCollection<T> {
type Output = T;
fn index(&self, id: &Id<T>) -> &Self::Output {
self.0.get(id).unwrap()
}
}
impl<T> std::ops::IndexMut<&Id<T>> for IdCollection<T> {
fn index_mut(&mut self, id: &Id<T>) -> &mut Self::Output {
self.0.get_mut(id).unwrap()
}
}
If I understand correctly what you try to achieve, then I have to tell you, that it is a bit more complex than you originally thought it would be.
First of all, you have to realise, that if you like to use a HashMap then the type of the key required to be hashable and comparable. Therefore the generic type parameter T in Id<T> has to be bound to those traits in order to make Id hashable and comparable.
The second thing you need to understand is that there are two different traits to deal with the indexing operator: Index for immutable data access, and IndexMut for mutable one.
use std::{
marker::PhantomData,
collections::HashMap,
cmp::{
Eq,
PartialEq,
},
ops::{
Index,
IndexMut,
},
hash::Hash,
};
#[derive(PartialEq, Hash)]
struct Id<T>(usize, PhantomData<T>)
where T: PartialEq + Hash;
impl<T> Eq for Id<T>
where T: PartialEq + Hash
{}
struct IdCollection<T>(HashMap<Id<T>, T>)
where T: PartialEq + Hash;
impl<T> Index<Id<T>> for IdCollection<T>
where T: PartialEq + Hash
{
type Output = T;
fn index(&self, id: Id<T>) -> &Self::Output
{
self.0.get(&id).unwrap()
}
}
impl<T> IndexMut<Id<T>> for IdCollection<T>
where T: PartialEq + Hash
{
fn index_mut(&mut self, id: Id<T>) -> &mut Self::Output
{
self.0.get_mut(&id).unwrap()
}
}
fn main()
{
let mut i = IdCollection(HashMap::new());
i.0.insert(Id(12, PhantomData), 99i32);
println!("{:?}", i[Id(12, PhantomData)]);
i[Id(12, PhantomData)] = 54i32;
println!("{:?}", i[Id(12, PhantomData)]);
}
It may seem a bit surprising, but IndexMut is not designed to insert an element into the collection but to actually modify an existing one. That's the main reason why HashMap does not implement IndexMut -- and that's also the reason why the above example uses the HashMap::insert method to initially place the data. As you can see, later on, when the value is already available we can modify it via the IdCollection::index_mut.

How can I explicitly specify a lifetime when implementing a trait?

Given the implementation below, where essentially I have some collection of items that can be looked up via either a i32 id field or a string field. To be able to use either interchangeably, a trait "IntoKey" is used, and a match dispatches to the appropriate lookup map; this all works fine for my definition of get within the MapCollection impl:
use std::collections::HashMap;
use std::ops::Index;
enum Key<'a> {
I32Key(&'a i32),
StringKey(&'a String),
}
trait IntoKey<'a> {
fn into_key(&'a self) -> Key<'a>;
}
impl<'a> IntoKey<'a> for i32 {
fn into_key(&'a self) -> Key<'a> { Key::I32Key(self) }
}
impl<'a> IntoKey<'a> for String {
fn into_key(&'a self) -> Key<'a> { Key::StringKey(self) }
}
#[derive(Debug)]
struct Bar {
i: i32,
n: String,
}
struct MapCollection
{
items: Vec<Bar>,
id_map: HashMap<i32, usize>,
name_map: HashMap<String, usize>,
}
impl MapCollection {
fn new(items: Vec<Bar>) -> MapCollection {
let mut is = HashMap::new();
let mut ns = HashMap::new();
for (idx, item) in items.iter().enumerate() {
is.insert(item.i, idx);
ns.insert(item.n.clone(), idx);
}
MapCollection {
items: items,
id_map: is,
name_map: ns,
}
}
fn get<'a, K>(&self, key: &'a K) -> Option<&Bar>
where K: IntoKey<'a> //'
{
match key.into_key() {
Key::I32Key(i) => self.id_map.get(i).and_then(|idx| self.items.get(*idx)),
Key::StringKey(s) => self.name_map.get(s).and_then(|idx| self.items.get(*idx)),
}
}
}
fn main() {
let bars = vec![Bar { i:1, n:"foo".to_string() }, Bar { i:2, n:"far".to_string() }];
let map = MapCollection::new(bars);
if let Some(bar) = map.get(&1) {
println!("{:?}", bar);
}
if map.get(&3).is_none() {
println!("no item numbered 3");
}
if let Some(bar) = map.get(&"far".to_string()) {
println!("{:?}", bar);
}
if map.get(&"baz".to_string()).is_none() {
println!("no item named baz");
}
}
However, if I then want to implement std::ops::Index for this struct, if I attempt to do the below:
impl<'a, K> Index<K> for MapCollection
where K: IntoKey<'a> {
type Output = Bar;
fn index<'b>(&'b self, k: &K) -> &'b Bar {
self.get(k).expect("no element")
}
}
I hit a compiler error:
src/main.rs:70:18: 70:19 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
src/main.rs:70 self.get(k).expect("no element")
^
src/main.rs:69:5: 71:6 help: consider using an explicit lifetime parameter as shown: fn index<'b>(&'b self, k: &'a K) -> &'b Bar
src/main.rs:69 fn index<'b>(&'b self, k: &K) -> &'b Bar {
src/main.rs:70 self.get(k).expect("no element")
src/main.rs:71 }
I can find no way to specify a distinct lifetime here; following the compiler's recommendation is not permitted as it changes the function signature and no longer matches the trait, and anything else I try fails to satisfy the lifetime specification.
I understand that I can implement the trait for each case (i32, String) separately instead of trying to implement it once for IntoKey, but I am more generally trying to understand lifetimes and appropriate usage. Essentially:
Is there actually an issue the compiler is preventing? Is there something unsound about this approach?
Am I specifying my lifetimes incorrectly? To me, the lifetime 'a in Key/IntoKey is dictating that the reference need only live long enough to do the lookup; the lifetime 'b associated with the index fn is stating that the reference resulting from the lookup will live as long as the containing MapCollection.
Or am I simply not utilizing the correct syntax to specify the needed information?
(using rustc 1.0.0-nightly (b63cee4a1 2015-02-14 17:01:11 +0000))
Do you intend on implementing IntoKey on struct's that are going to store references of lifetime 'a? If not, you can change your trait and its implementations to:
trait IntoKey {
fn into_key<'a>(&'a self) -> Key<'a>;
}
This is the generally recommended definition style, if you can use it. If you can't...
Let's look at this smaller reproduction:
use std::collections::HashMap;
use std::ops::Index;
struct Key<'a>(&'a u8);
trait IntoKey<'a> { //'
fn into_key(&'a self) -> Key<'a>;
}
struct MapCollection;
impl MapCollection {
fn get<'a, K>(&self, key: &'a K) -> &u8
where K: IntoKey<'a> //'
{
unimplemented!()
}
}
impl<'a, K> Index<K> for MapCollection //'
where K: IntoKey<'a> //'
{
type Output = u8;
fn index<'b>(&'b self, k: &K) -> &'b u8 { //'
self.get(k)
}
}
fn main() {
}
The problem lies in get:
fn get<'a, K>(&self, key: &'a K) -> &u8
where K: IntoKey<'a>
Here, we are taking a reference to K that must live as long as the Key we get out of it. However, the Index trait doesn't guarantee that:
fn index<'b>(&'b self, k: &K) -> &'b u8
You can fix this by simply giving a fresh lifetime to key:
fn get<'a, 'b, K>(&self, key: &'b K) -> &u8
where K: IntoKey<'a>
Or more succinctly:
fn get<'a, K>(&self, key: &K) -> &u8
where K: IntoKey<'a>

Resources