浏览代码

Merge pull request #1332 from Demonthos/fix-variance

Separate Parent and Child Component Lifetimes
Jonathan Kelley 1 年之前
父节点
当前提交
74211a6b61

+ 1 - 0
packages/core/Cargo.toml

@@ -39,6 +39,7 @@ dioxus = { workspace = true }
 pretty_assertions = "1.3.0"
 pretty_assertions = "1.3.0"
 rand = "0.8.5"
 rand = "0.8.5"
 dioxus-ssr = { workspace = true }
 dioxus-ssr = { workspace = true }
+trybuild = "1.0"
 
 
 [features]
 [features]
 default = []
 default = []

+ 32 - 0
packages/core/compile_tests/props_safety.rs

@@ -0,0 +1,32 @@
+use dioxus::prelude::*;
+
+fn main() {}
+
+fn app(cx: Scope) -> Element {
+    let count: &RefCell<Vec<Element>> = cx.use_hook(|| RefCell::new(Vec::new()));
+
+    render! {
+        unsafe_child_component {
+            borrowed: count
+        }
+    }
+}
+
+#[derive(Props)]
+struct Testing<'a> {
+    borrowed: &'a RefCell<Vec<Element<'a>>>,
+}
+
+fn unsafe_child_component<'a>(cx: Scope<'a, Testing<'a>>) -> Element<'a> {
+    let Testing { borrowed } = cx.props;
+    let borrowed_temporary_data =
+        cx.use_hook(|| String::from("This data is only valid for the lifetime of the child"));
+
+    borrowed
+        .borrow_mut()
+        .push(render! {"{borrowed_temporary_data}"});
+
+    cx.render(rsx! {
+        div { "Hello, world!" }
+    })
+}

+ 20 - 0
packages/core/compile_tests/props_safety.stderr

@@ -0,0 +1,20 @@
+error[E0521]: borrowed data escapes outside of function
+  --> compile_tests/props_safety.rs:8:5
+   |
+5  |   fn app(cx: Scope) -> Element {
+   |          --
+   |          |
+   |          `cx` is a reference that is only valid in the function body
+   |          has type `&'1 Scoped<'1>`
+...
+8  | /     render! {
+9  | |         unsafe_child_component {
+10 | |             borrowed: count
+11 | |         }
+12 | |     }
+   | |     ^
+   | |     |
+   | |_____`cx` escapes the function body here
+   |       argument requires that `'1` must outlive `'static`
+   |
+   = note: this error originates in the macro `render` (in Nightly builds, run with -Z macro-backtrace for more info)

+ 7 - 0
packages/core/src/properties.rs

@@ -73,3 +73,10 @@ impl EmptyBuilder {
 pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element<'a>) -> T::Builder {
 pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element<'a>) -> T::Builder {
     T::builder()
     T::builder()
 }
 }
+
+#[cfg(not(miri))]
+#[test]
+fn unsafe_props_fail() {
+    let t = trybuild::TestCases::new();
+    t.compile_fail("compile_tests/props_safety.rs");
+}

+ 5 - 4
packages/core/src/scopes.rs

@@ -481,19 +481,20 @@ impl<'src> ScopeState {
     /// fn(Scope<Props>) -> Element;
     /// fn(Scope<Props>) -> Element;
     /// async fn(Scope<Props<'_>>) -> Element;
     /// async fn(Scope<Props<'_>>) -> Element;
     /// ```
     /// ```
-    pub fn component<P>(
+    pub fn component<'child, P>(
         &'src self,
         &'src self,
-        component: fn(Scope<'src, P>) -> Element<'src>,
+        component: fn(Scope<'child, P>) -> Element<'child>,
         props: P,
         props: P,
         fn_name: &'static str,
         fn_name: &'static str,
     ) -> DynamicNode<'src>
     ) -> DynamicNode<'src>
     where
     where
-        P: Properties + 'src,
+        P: Properties + 'child,
+        'src: 'child,
     {
     {
         let vcomp = VProps::new(component, P::memoize, props);
         let vcomp = VProps::new(component, P::memoize, props);
 
 
         // cast off the lifetime of the render return
         // cast off the lifetime of the render return
-        let as_dyn: Box<dyn AnyProps<'src> + '_> = Box::new(vcomp);
+        let as_dyn: Box<dyn AnyProps<'child> + '_> = Box::new(vcomp);
         let extended: Box<dyn AnyProps<'src> + 'src> = unsafe { std::mem::transmute(as_dyn) };
         let extended: Box<dyn AnyProps<'src> + 'src> = unsafe { std::mem::transmute(as_dyn) };
 
 
         DynamicNode::Component(VComponent {
         DynamicNode::Component(VComponent {