Is it possible to change generic lifetime of a struct? - rust

I have an existing library which exposes a necessary data via reference to a struct with lifetime parameter. The problem is that I need to expose it using wasm_bindgen. For that I've created a separate struct in my wasm module:
#[wasm_bindgen]
struct Event {
// doesn't compile - passed from another lib
inner: &'a InnerEvent<'b>
}
impl<'a,'b> From<&'a InnerEvent<'b>> for Event {
fn from(e: &'a InnerEvent<'b>) -> Self {
Event { inner: e }
}
}
Now, the problem is that 'a and 'b are lifetimes that are passed from an outer library, but wasm-bindgen limitations doesn't allow for a Rust struct that's about to be accessible via WebAssembly to contain any generic params on its own - therefore Event cannot define any references to 'a or 'b.
While I can get rid of 'a by using raw pointers, I cannot do the same with 'b. Technically I can mock it with 'static, however I'm unable to change struct's generic lifetime param from 'a to 'static.
How can I keep reference to an &'a InnerEvent<'b> in a way that doesn't collide with wasm-bindgen restrictions?
EDIT:
My particular use case looks like follows - I'm writing a wrapper over a library, that enables a pub/sub for custom user data:
pub struct Transaction<'txn> {
// .. omitted fields
}
pub struct SomeStruct {
pub fn subscribe<F>(&mut self, f: F) -> Subscription
where F: Fn(&Transaction, &Event) -> () + 'static {
// .. method body
}
}
Now I need to expose SomeStruct over to WebAssembly. For this I'm creating a wasm-bingen wrapper, and I need it to be able to expose its subscribe capabilities over to a JavaScript side:
#[wasm_bindgen]
pub struct SomeStructWrapper(SomeStruct);
#[wasm_bindgen]
impl SomeStructWrapper {
#[wasm_bindgen]
pub fn observe(&mut self, f: js_sys::Function) -> SubscriptionWrapper {
let sub = self.0.observe(move |transaction, event| {
// transaction is: &'a Transaction<'txn> and event: &'a Event
let e = EventWrapper::new(e, txn);
let arg: JsValue = e.into();
f.call1(&JsValue::UNDEFINED, &arg);
});
SubscriptionWrapper(sub)
}
}
#[wasm_bindgen]
pub struct SubscriptionWrapper(Subscription);
Now the problem is that I need to references to both of the rust callback parameters (transaction and event) inside of JavaScript callback. This means that EventWrapper needs to store them as fields:
// code below won't compile because
// wasm_bindgen doesn't allow its structs to declare lifecycle parameters
#[wasm_bindgen]
pub struct EventWrapper {
transaction: &'a Transaction<'txn>,
inner_event: &'a Event,
}
// we can get rid of 'a by casting references to raw pointers
// but this won't fix issue with 'txn lifecycle
#[wasm_bindgen]
pub struct EventWrapper {
transaction: *const Transaction<'txn>,
inner_event: *const Event,
}

In the below code, it get error `error: structs with #[wasm_bindgen] cannot have lifetime or type parameters currently?
use wasm_bindgen::prelude::*;
struct InnerEvent<'a> {
_a: &'a str
}
#[wasm_bindgen]
struct Event<'a,'b> where 'a: 'b {
inner: &'a InnerEvent<'b>
}
impl<'a,'b> From<&'a InnerEvent<'b>> for Event<'a,'b> {
fn from(e: &'a InnerEvent<'b>) -> Self {
Event { inner: e }
}
}
Output
error: structs with #[wasm_bindgen] cannot have lifetime or type parameters currently
--> src\lib.rs:8:13
|
8 | struct Event<'a,'b> where 'a: 'b {
| ^^^^^^^
You can get around like that
use wasm_bindgen::prelude::*;
struct InnerEvent<'a> {
_a: &'a str
}
struct Event<'a,'b> where 'a: 'b {
inner: &'a InnerEvent<'b>
}
impl<'a,'b> From<&'a InnerEvent<'b>> for Event<'a,'b> {
fn from(e: &'a InnerEvent<'b>) -> Self {
Event { inner: e }
}
}
#[wasm_bindgen]
struct WrapEvent
{
i: Event<'static, 'static>
}
But this meet your requierement?
This issue is also discussed here How to get rid of lifetime in wrapper struct for wasm_bindgen

Related

Using trait objects on types with Default

I'm trying to use a trait object in a hashmap so I can call an update function.
This here is really the code I'm trying to write. I am pretty sure that I can change this code to get this to work the way that I want as-is, but I figured I can turn this into a learning opportunity because the Sized stuff is not quite clear to me.
let mut hm: HashMap<SomeEnum, Box<dyn SomeTrait>> = HashMap::new();
hm.entry(SomeEnum::Type1).and_modify(|b| b.some_other_func()).or_insert(/* HOW */ default());
But I don't really understand the Sized restrictions on trait objects or what Sized is (or why using Default as a Supertrait prevents the object from being Sized). If the entry does not exist in the hashmap, I would like to add the default version of that object to the entry.
Here's an reprex showing my issue.
use std::collections::HashMap;
#[derive(PartialEq, Eq, Hash)]
enum SomeEnum {
Type1,
Type2,
}
// SomeTrait is a subtrait of Default
// Because I want every type to implement Default
trait SomeTrait: Default {
fn new() -> Self {
Self {
..Default::default()
}
}
fn some_other_func(&self);
}
// SomeStruct implements Default and SomeTrait
struct SomeStruct {
foo: i32,
}
impl Default for SomeStruct {
fn default() -> Self {
SomeStruct { foo: 10 }
}
}
impl SomeTrait for SomeStruct {
fn some_other_func(&self) {}
}
fn main() {
let mut hm: HashMap<SomeEnum, Box<dyn SomeTrait>> = HashMap::new();
hm.entry(SomeEnum::Type1)
.and_modify(|b| b.some_other_func())
.or_insert(/* HOW */ default());
}
I cannot use a generic type instead of the trait object because I do/will have multiple implementations of this trait.
I have also tried creating a new trait that is a subtrait of both Default and the one I want:
trait AutoSomeType: SomeType + Default {}
trait SomeType {
fn new() -> Self
where
Self: Sized,
{
Self {
..Default::default()
}
}
fn some_other_func(&self);
}
I got here somehow based on a compiler recommendation, but I feel like this isn't on the right track to a solution.

Immutable struct with mutable reference members

Is my understanding correct that in Rust it is not possible to protect reference members of a struct from modification while having the reference target values mutable? (Without runtime borrow checking that is.) For example:
struct MyData<'a> {
pub some_ref: &'a mut i32,
}
fn doit<'a>(data: &mut MyData<'a>, other_ref: &'a mut i32) {
// I want to be able to do the following here:
*data.some_ref = 22;
// but make it impossible to do the following:
data.some_ref = other_ref;
}
Not being able to change the reference value may be useful in certain FFI situations. FFI and the performance requirements reasons prevent the use of runtime borrow checking here.
In C++ it can be expressed like this:
struct MyData {
int* const some_ref;
};
void doit(const MyData &data, int* other_ref) {
// this is allowed:
*data.some_ref = 22;
// this is not:
data.some_ref = other_ref; // compile error
}
You can create a wrapper type around the reference. If the constructor is private, and so is the wrapped reference field, you cannot replace the reference itself. You can then implement DerefMut to allow changing the referent.
pub struct ImmRef<'a> {
inner: &'a mut i32,
}
impl<'a> ImmRef<'a> {
fn new(inner: &'a mut i32) -> Self { Self { inner } }
}
impl std::ops::Deref for ImmRef<'_> {
type Target = i32;
fn deref(&self) -> &Self::Target { &*self.inner }
}
impl std::ops::DerefMut for ImmRef<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.inner }
}
struct MyData<'a> {
pub some_ref: ImmRef<'a>,
}
fn doit<'a>(data: &mut MyData<'a>, other_ref: &'a mut i32) {
// I want to be able to do the following here:
*data.some_ref = 22;
// but make it impossible to do the following:
// data.some_ref = other_ref;
}
You can mark the newtype #[repr(transparent)] for FFI purposes.
But do note that if the code has some ImmRef<'a> available it can use tools such as std::mem::replace() to replace the reference.
Rust does not allow you to specify the mutability of individual fields like you can via const in C++. Instead, you should simply encapsulate the data by making it private and only allow modification through methods that you dictate:
struct MyData<'a> {
some_ref: &'a mut i32,
}
impl MyData<'_> {
pub fn set_ref(&mut self, other: i32) {
*self.some_ref = other;
}
}
That way, the field some_ref cannot be modified directly (outside of the module) and must use the available method.

Access a composition field of a struct from Trait

In the following example, is there any way to access the comp_field within the implementation of BaseTrait for MyStruct?
pub trait CompositionTrait {
//methods
}
pub trait BaseTrait {
fn get_comp_field(&self) -> Box<dyn CompositionTrait>;
}
pub struct MyStruct {
pub comp_field: Box<dyn CompositionTrait>
}
impl MyStruct {
pub fn new(config: Config) -> Result<Self, Error> {
// here based on config, comp_field is assigned with different structs, all of which implements CompositionTrait.
}
}
impl BaseTrait for MyStruct {
fn get_comp_field(&self) -> Box<dyn CompositionTrait> {
self.comp_field // Error
}
}
The following error is presented for the current implementation:
cannot move out of `self.comp_field` which is behind a shared reference
move occurs because `self.comp_field` has type `std::boxed::Box<dyn BaseTrait::CompositionTrait>`, which does not implement the `Copy` trait
The eventual need for such implementation would be accessing the methods of comp_field in the functions which are based on BaseTrait.
The error occurs because you're trying to move the box out of the struct, but the struct is not mutable (and even if it was you would need to put something else in place of the box you're removing). Instead you should return a reference to the CompositionTrait:
pub trait CompositionTrait {
//methods
}
pub trait BaseTrait {
fn get_comp_field(&self) -> &dyn CompositionTrait;
}
pub struct MyStruct {
pub comp_field: Box<dyn CompositionTrait>
}
impl BaseTrait for MyStruct {
fn get_comp_field(&self) -> &dyn CompositionTrait {
&*self.comp_field // Or self.comp_field.as_ref()
}
}
Playground

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>,
}

Two similar code snippets but only one triggers a lifetime error

#![feature(rustc_private)]
extern crate rustc;
use rustc::hir::intravisit as hir_visit;
use rustc::hir;
use std::marker::PhantomData;
// ----------------------------------------------------------------------------
// Why does this compile?
// ----------------------------------------------------------------------------
pub struct Map<'a> {
pub _m: PhantomData<&'a ()>,
}
pub struct SomeVisitor<'a, 'tcx: 'a> {
pub map: &'a Map<'tcx>,
}
pub enum NestedVisitorMap<'this, 'tcx: 'this> {
None,
OnlyBodies(&'this Map<'tcx>),
All(&'this Map<'tcx>),
}
pub trait Visitor<'v>: Sized {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v>;
}
impl<'v, 'tcx> Visitor<'v> for SomeVisitor<'v, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
NestedVisitorMap::All(self.map)
}
}
// ----------------------------------------------------------------------------
// Why does this *not* compile?
// ----------------------------------------------------------------------------
pub struct SomeVisitor2<'a, 'tcx: 'a> {
pub map: &'a hir::map::Map<'tcx>,
}
impl<'v, 'tcx> hir_visit::Visitor<'v> for SomeVisitor2<'v, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, 'v> {
hir_visit::NestedVisitorMap::All(self.map)
}
}
fn main() {}
playground
NestedVisitorMap and Visitor
I recently ran into a lifetime issue and I decided to recreate it without any dependencies. The odd thing is that I can not recreate the lifetime error. To me both implementations look the same from the outside, but only one compiles successfully. Why is that?
rustc 1.21.0-nightly (e26688824 2017-08-27)
Update:
The problem seems to be the RefCell inside Map.
#[derive(Clone)]
pub struct Map<'hir> {
inlined_bodies: RefCell<rustc::util::nodemap::DefIdMap<&'hir hir::Body>>,
}
If there is a RefCell with a inner lifetime, it will trigger an error.
playground
Update2:
It turns out that I just mixed up lifetime subtyping. playground
I still don't know why only RefCell causes the error.

Resources