I am having trouble figuring out what lifetime parameter will work for this, so my current workarounds include transmutes or raw pointers. I have a structure holding a function pointer with a generic as a parameter:
struct CB<Data> {
cb: fn(Data) -> usize
}
I would like to store an instance of that, parameterized by some type containing a reference, in some other structure that implements a trait with one method, and use that trait method to call the function pointer in CB.
struct Holder<'a> {
c: CB<Option<&'a usize>>
}
trait Exec {
fn exec(&self, v: &usize) -> usize;
}
impl<'a> Holder<'a> {
fn exec_aux(&self, v: &'a usize) -> usize {
(self.c.cb)(Some(v))
}
}
impl<'a> Exec for Holder<'a> {
fn exec(&self, v: &usize) -> usize
{
self.exec_aux(v)
}
}
This gives me a lifetime error for the 'Exec' impl of Holder:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
Simply calling exec_aux works fine as long as I don't define that Exec impl:
fn main() {
let h = Holder { c: CB{cb:cbf}};
let v = 12;
println!("{}", h.exec_aux(&v));
}
Also, making CB not generic also makes this work:
struct CB {
cb: fn(Option<&usize>) -> usize
}
The parameter in my actual code is not a usize but something big that I would rather not copy.
The lifetimes in your Exec trait are implicitly this:
trait Exec {
fn exec<'s, 'a>(&'s self, v: &'a usize) -> usize;
}
In other words, types that implement Exec need to accept any lifetimes 's and 'a. However, your Holder::exec_aux method expects a specific lifetime 'a that's tied to the lifetime parameter of the Holder type.
To make this work, you need to add 'a as a lifetime parameter to the Exec trait instead, so that you can implement the trait specifically for that lifetime:
trait Exec<'a> {
// ^^^^ vv
fn exec(&self, v: &'a usize) -> usize;
}
impl<'a> Exec<'a> for Holder<'a> {
// ^^^^ vv
fn exec(&self, v: &'a usize) -> usize
{
self.exec_aux(v)
}
}
The problem here is that the Exec trait is too generic to be used in this way by Holder. First, consider the definition:
trait Exec {
fn exec(&self, v: &usize) -> usize;
}
This definition will cause the compiler to automatically assign two anonymous lifetimes for &self and &v in exec. It's basically the same as
fn exec<'a, 'b>(&'a self, v: &'b usize) -> usize;
Note that there is no restriction on who needs to outlive whom, the references just need to be alive for the duration of the method call.
Now consider the definition
impl<'a> Holder<'a> {
fn exec_aux(&self, v: &'a usize) -> usize {
// ... doesn't matter
}
}
Since we know that &self is a &Holder<'a> (this is what the impl refers to), we need to have at least a &'a Holder<'a> here, because &'_ self can't have a lifetime shorter than 'a in Holder<'a>. So this is saying that the two parameters have the same lifetime: &'a self, &'a usize.
Where it all goes wrong is when you try to combine the two. The trait forces you into the following signature, which (again) has two distinct implicit lifetimes. But the actual Holder which you then try to call a method on forces you to have the same lifetimes for &self and &v.
fn exec(&self, v: &usize) -> usize {
// Holder<'a> needs `v` to be `'a` when calling exec_aux
// But the trait doesn't say so.
self.exec_aux(v)
}
One solution is to redefine the trait as
trait Exec<'a> {
fn exec(&'a self, v: &'a usize) -> usize;
}
and then implement it as
impl<'a> Exec<'a> for Holder<'a> {
fn exec(&'a self, v: &'a usize) -> usize {
self.exec_aux(v)
}
}
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.
I try to write my own RefCell-like mutable memory location but without runtime borrow checking (no overhead). I adopted the code architecture from RefCell (and Ref, and RefMut). I can call .borrow() without problems but if I call .borrow_mut() then the rust compiler says cannot borrow as mutable. I don't see the problem, my .borrow_mut() impl looks fine?
code that fails:
let real_refcell= Rc::from(RefCell::from(MyStruct::new()));
let nooverhead_refcell = Rc::from(NORefCell::from(MyStruct::new()));
// works
let refmut_refcell = real_refcell.borrow_mut();
// cannot borrow as mutable
let refmut_norefcell = nooverhead_refcell.borrow_mut();
norc.rs (No Overhead RefCell)
use crate::norc_ref::{NORefMut, NORef};
use std::cell::UnsafeCell;
use std::borrow::Borrow;
#[derive(Debug)]
pub struct NORefCell<T: ?Sized> {
value: UnsafeCell<T>
}
impl<T> NORefCell<T> {
pub fn from(t: T) -> NORefCell<T> {
NORefCell {
value: UnsafeCell::from(t)
}
}
pub fn borrow(&self) -> NORef<'_, T> {
NORef {
value: unsafe { &*self.value.get() }
}
}
pub fn borrow_mut(&mut self) -> NORefMut<'_, T> {
NORefMut {
value: unsafe { &mut *self.value.get() }
}
}
}
norc_ref.rs (data structure returned by NORefCell.borrow[_mut]()
use std::ops::{Deref, DerefMut};
#[derive(Debug)]
pub struct NORef<'b, T: ?Sized + 'b> {
pub value: &'b T,
}
impl<T: ?Sized> Deref for NORef<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.value
}
}
/// No Overhead Ref Cell: Mutable Reference
#[derive(Debug)]
pub struct NORefMut<'b, T: ?Sized + 'b> {
pub value: &'b mut T,
}
impl<T: ?Sized> Deref for NORefMut<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.value
}
}
impl<T: ?Sized> DerefMut for NORefMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
self.value
}
}
NORefCell::borrow_mut() takes &mut self, which requires a DerefMut on the Rc in which it is wrapped. This won't work because Rc does not give mutable references just by asking nicely (you need it to check if the reference count is exactly one, otherwise there would be multiple mutable borrows).
borrow_mut has to take &self instead of &mut self.
As mentioned in my comment: What you are basically doing is providing a safe-looking abstraction around an UnsafeCell. This is incredibly dangerous. Notice the docs regarding UnsafeCell:
The compiler makes optimizations based on the knowledge that &T is not mutably aliased or mutated, and that &mut T is unique. UnsafeCell is the only core language feature to work around the restriction that &T may not be mutated.
You are providing a thin wrapper around this powerful object, with no unsafe on the API-boundary. The "No-overhead-RefCell" is really a "no-trigger-guard-foot-gun". It does work, yet be warned about its dangers.
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(),
}
}
}
I understand how lifetime parameters apply to functions and structs, but what does it mean for a trait to have a lifetime parameter? Is it a shortcut to introduce a lifetime parameter to its methods, or is it something else?
If you have a trait with a lifetime bound, then implementors of the trait can participate in the same lifetime. Concretely, this allows you to store references with that lifetime. It is not a shortcut for specifying lifetimes on member methods, and difficulty and confusing error messages lie that way!
trait Keeper<'a> {
fn save(&mut self, v: &'a u8);
fn restore(&self) -> &'a u8;
}
struct SimpleKeeper<'a> {
val: &'a u8,
}
impl<'a> Keeper<'a> for SimpleKeeper<'a> {
fn save(&mut self, v: &'a u8) {
self.val = v
}
fn restore(&self) -> &'a u8 {
self.val
}
}
Note how both the struct and the trait are parameterized on a lifetime, and that lifetime is the same.
What would the non-trait versions of save() and restore() look like for SimpleKeeper<'a>?
Very similar, actually. The important part is that the struct stores the reference itself, so it needs to have a lifetime parameter for the values inside.
struct SimpleKeeper<'a> {
val: &'a u8,
}
impl<'a> SimpleKeeper<'a> {
fn save(&mut self, v: &'a u8) {
self.val = v
}
fn restore(&self) -> &'a u8 {
self.val
}
}
And would they mean exactly the same thing as the the trait version?
Yep!