From bf871ffd7f910601483b4eec5e4fbeb29f29a17b Mon Sep 17 00:00:00 2001 From: Matt Mastracci Date: Fri, 28 Sep 2018 21:11:05 -0600 Subject: [PATCH] Remove lifetime specifier on table getter methods (#4949) With the old-style code, the test fails with a borrow-checker error: ``` #[inline] pub fn name(&'a self) -> &'a str { self._tab.get::>(Monster::VT_NAME, None).unwrap() } ``` ``` error[E0597]: `e` does not live long enough --> tests/integration_test.rs:273:57 | 273 | let enemy_of_my_enemy = monster.enemy().map(|e| e.name()); | ^ - `e` dropped here while still borrowed | | | borrowed value does not live long enough 274 | assert_eq!(enemy_of_my_enemy, Some("Fred")); 275 | } | - borrowed value needs to live until here ``` --- src/idl_gen_rust.cpp | 2 +- tests/monster_test_generated.rs | 118 +++++++++--------- .../namespace_test1_generated.rs | 2 +- .../namespace_test2_generated.rs | 12 +- .../rust_usage_test/tests/integration_test.rs | 17 +++ 5 files changed, 84 insertions(+), 67 deletions(-) diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp index 1ca6254a9..48858bdad 100644 --- a/src/idl_gen_rust.cpp +++ b/src/idl_gen_rust.cpp @@ -1263,7 +1263,7 @@ class RustGenerator : public BaseGenerator { GenComment(field.doc_comment, " "); code_ += " #[inline]"; - code_ += " pub fn {{FIELD_NAME}}(&'a self) -> {{RETURN_TYPE}} {"; + code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {"; code_ += " {{FUNC_BODY}}"; code_ += " }"; diff --git a/tests/monster_test_generated.rs b/tests/monster_test_generated.rs index 020a543eb..d61ea35f3 100644 --- a/tests/monster_test_generated.rs +++ b/tests/monster_test_generated.rs @@ -555,7 +555,7 @@ impl<'a> TestSimpleTableWithEnum<'a> { pub const VT_COLOR: flatbuffers::VOffsetT = 4; #[inline] - pub fn color(&'a self) -> Color { + pub fn color(&self) -> Color { self._tab.get::(TestSimpleTableWithEnum::VT_COLOR, Some(Color::Green)).unwrap() } } @@ -635,15 +635,15 @@ impl<'a> Stat<'a> { pub const VT_COUNT: flatbuffers::VOffsetT = 8; #[inline] - pub fn id(&'a self) -> Option<&'a str> { + pub fn id(&self) -> Option<&'a str> { self._tab.get::>(Stat::VT_ID, None) } #[inline] - pub fn val(&'a self) -> i64 { + pub fn val(&self) -> i64 { self._tab.get::(Stat::VT_VAL, Some(0)).unwrap() } #[inline] - pub fn count(&'a self) -> u16 { + pub fn count(&self) -> u16 { self._tab.get::(Stat::VT_COUNT, Some(0)).unwrap() } } @@ -731,7 +731,7 @@ impl<'a> Referrable<'a> { pub const VT_ID: flatbuffers::VOffsetT = 4; #[inline] - pub fn id(&'a self) -> u64 { + pub fn id(&self) -> u64 { self._tab.get::(Referrable::VT_ID, Some(0)).unwrap() } #[inline] @@ -900,19 +900,19 @@ impl<'a> Monster<'a> { pub const VT_VECTOR_OF_NON_OWNING_REFERENCES: flatbuffers::VOffsetT = 88; #[inline] - pub fn pos(&'a self) -> Option<&'a Vec3> { + pub fn pos(&self) -> Option<&'a Vec3> { self._tab.get::(Monster::VT_POS, None) } #[inline] - pub fn mana(&'a self) -> i16 { + pub fn mana(&self) -> i16 { self._tab.get::(Monster::VT_MANA, Some(150)).unwrap() } #[inline] - pub fn hp(&'a self) -> i16 { + pub fn hp(&self) -> i16 { self._tab.get::(Monster::VT_HP, Some(100)).unwrap() } #[inline] - pub fn name(&'a self) -> &'a str { + pub fn name(&self) -> &'a str { self._tab.get::>(Monster::VT_NAME, None).unwrap() } #[inline] @@ -926,41 +926,41 @@ impl<'a> Monster<'a> { key.cmp(&val) } #[inline] - pub fn inventory(&'a self) -> Option<&'a [u8]> { + pub fn inventory(&self) -> Option<&'a [u8]> { self._tab.get::>>(Monster::VT_INVENTORY, None).map(|v| v.safe_slice()) } #[inline] - pub fn color(&'a self) -> Color { + pub fn color(&self) -> Color { self._tab.get::(Monster::VT_COLOR, Some(Color::Blue)).unwrap() } #[inline] - pub fn test_type(&'a self) -> Any { + pub fn test_type(&self) -> Any { self._tab.get::(Monster::VT_TEST_TYPE, Some(Any::NONE)).unwrap() } #[inline] - pub fn test(&'a self) -> Option> { + pub fn test(&self) -> Option> { self._tab.get::>>(Monster::VT_TEST, None) } #[inline] - pub fn test4(&'a self) -> Option<&'a [Test]> { + pub fn test4(&self) -> Option<&'a [Test]> { self._tab.get::>>(Monster::VT_TEST4, None).map(|v| v.safe_slice() ) } #[inline] - pub fn testarrayofstring(&'a self) -> Option>> { + pub fn testarrayofstring(&self) -> Option>> { self._tab.get::>>>(Monster::VT_TESTARRAYOFSTRING, None) } /// an example documentation comment: this will end up in the generated code /// multiline too #[inline] - pub fn testarrayoftables(&'a self) -> Option>>> { + pub fn testarrayoftables(&self) -> Option>>> { self._tab.get::>>>>(Monster::VT_TESTARRAYOFTABLES, None) } #[inline] - pub fn enemy(&'a self) -> Option> { + pub fn enemy(&self) -> Option> { self._tab.get::>>(Monster::VT_ENEMY, None) } #[inline] - pub fn testnestedflatbuffer(&'a self) -> Option<&'a [u8]> { + pub fn testnestedflatbuffer(&self) -> Option<&'a [u8]> { self._tab.get::>>(Monster::VT_TESTNESTEDFLATBUFFER, None).map(|v| v.safe_slice()) } pub fn testnestedflatbuffer_nested_flatbuffer(&'a self) -> Option> { @@ -973,119 +973,119 @@ impl<'a> Monster<'a> { } } #[inline] - pub fn testempty(&'a self) -> Option> { + pub fn testempty(&self) -> Option> { self._tab.get::>>(Monster::VT_TESTEMPTY, None) } #[inline] - pub fn testbool(&'a self) -> bool { + pub fn testbool(&self) -> bool { self._tab.get::(Monster::VT_TESTBOOL, Some(false)).unwrap() } #[inline] - pub fn testhashs32_fnv1(&'a self) -> i32 { + pub fn testhashs32_fnv1(&self) -> i32 { self._tab.get::(Monster::VT_TESTHASHS32_FNV1, Some(0)).unwrap() } #[inline] - pub fn testhashu32_fnv1(&'a self) -> u32 { + pub fn testhashu32_fnv1(&self) -> u32 { self._tab.get::(Monster::VT_TESTHASHU32_FNV1, Some(0)).unwrap() } #[inline] - pub fn testhashs64_fnv1(&'a self) -> i64 { + pub fn testhashs64_fnv1(&self) -> i64 { self._tab.get::(Monster::VT_TESTHASHS64_FNV1, Some(0)).unwrap() } #[inline] - pub fn testhashu64_fnv1(&'a self) -> u64 { + pub fn testhashu64_fnv1(&self) -> u64 { self._tab.get::(Monster::VT_TESTHASHU64_FNV1, Some(0)).unwrap() } #[inline] - pub fn testhashs32_fnv1a(&'a self) -> i32 { + pub fn testhashs32_fnv1a(&self) -> i32 { self._tab.get::(Monster::VT_TESTHASHS32_FNV1A, Some(0)).unwrap() } #[inline] - pub fn testhashu32_fnv1a(&'a self) -> u32 { + pub fn testhashu32_fnv1a(&self) -> u32 { self._tab.get::(Monster::VT_TESTHASHU32_FNV1A, Some(0)).unwrap() } #[inline] - pub fn testhashs64_fnv1a(&'a self) -> i64 { + pub fn testhashs64_fnv1a(&self) -> i64 { self._tab.get::(Monster::VT_TESTHASHS64_FNV1A, Some(0)).unwrap() } #[inline] - pub fn testhashu64_fnv1a(&'a self) -> u64 { + pub fn testhashu64_fnv1a(&self) -> u64 { self._tab.get::(Monster::VT_TESTHASHU64_FNV1A, Some(0)).unwrap() } #[inline] - pub fn testarrayofbools(&'a self) -> Option<&'a [bool]> { + pub fn testarrayofbools(&self) -> Option<&'a [bool]> { self._tab.get::>>(Monster::VT_TESTARRAYOFBOOLS, None).map(|v| v.safe_slice()) } #[inline] - pub fn testf(&'a self) -> f32 { + pub fn testf(&self) -> f32 { self._tab.get::(Monster::VT_TESTF, Some(3.14159)).unwrap() } #[inline] - pub fn testf2(&'a self) -> f32 { + pub fn testf2(&self) -> f32 { self._tab.get::(Monster::VT_TESTF2, Some(3.0)).unwrap() } #[inline] - pub fn testf3(&'a self) -> f32 { + pub fn testf3(&self) -> f32 { self._tab.get::(Monster::VT_TESTF3, Some(0.0)).unwrap() } #[inline] - pub fn testarrayofstring2(&'a self) -> Option>> { + pub fn testarrayofstring2(&self) -> Option>> { self._tab.get::>>>(Monster::VT_TESTARRAYOFSTRING2, None) } #[inline] - pub fn testarrayofsortedstruct(&'a self) -> Option<&'a [Ability]> { + pub fn testarrayofsortedstruct(&self) -> Option<&'a [Ability]> { self._tab.get::>>(Monster::VT_TESTARRAYOFSORTEDSTRUCT, None).map(|v| v.safe_slice() ) } #[inline] - pub fn flex(&'a self) -> Option<&'a [u8]> { + pub fn flex(&self) -> Option<&'a [u8]> { self._tab.get::>>(Monster::VT_FLEX, None).map(|v| v.safe_slice()) } #[inline] - pub fn test5(&'a self) -> Option<&'a [Test]> { + pub fn test5(&self) -> Option<&'a [Test]> { self._tab.get::>>(Monster::VT_TEST5, None).map(|v| v.safe_slice() ) } #[inline] - pub fn vector_of_longs(&'a self) -> Option> { + pub fn vector_of_longs(&self) -> Option> { self._tab.get::>>(Monster::VT_VECTOR_OF_LONGS, None) } #[inline] - pub fn vector_of_doubles(&'a self) -> Option> { + pub fn vector_of_doubles(&self) -> Option> { self._tab.get::>>(Monster::VT_VECTOR_OF_DOUBLES, None) } #[inline] - pub fn parent_namespace_test(&'a self) -> Option> { + pub fn parent_namespace_test(&self) -> Option> { self._tab.get::>>(Monster::VT_PARENT_NAMESPACE_TEST, None) } #[inline] - pub fn vector_of_referrables(&'a self) -> Option>>> { + pub fn vector_of_referrables(&self) -> Option>>> { self._tab.get::>>>>(Monster::VT_VECTOR_OF_REFERRABLES, None) } #[inline] - pub fn single_weak_reference(&'a self) -> u64 { + pub fn single_weak_reference(&self) -> u64 { self._tab.get::(Monster::VT_SINGLE_WEAK_REFERENCE, Some(0)).unwrap() } #[inline] - pub fn vector_of_weak_references(&'a self) -> Option> { + pub fn vector_of_weak_references(&self) -> Option> { self._tab.get::>>(Monster::VT_VECTOR_OF_WEAK_REFERENCES, None) } #[inline] - pub fn vector_of_strong_referrables(&'a self) -> Option>>> { + pub fn vector_of_strong_referrables(&self) -> Option>>> { self._tab.get::>>>>(Monster::VT_VECTOR_OF_STRONG_REFERRABLES, None) } #[inline] - pub fn co_owning_reference(&'a self) -> u64 { + pub fn co_owning_reference(&self) -> u64 { self._tab.get::(Monster::VT_CO_OWNING_REFERENCE, Some(0)).unwrap() } #[inline] - pub fn vector_of_co_owning_references(&'a self) -> Option> { + pub fn vector_of_co_owning_references(&self) -> Option> { self._tab.get::>>(Monster::VT_VECTOR_OF_CO_OWNING_REFERENCES, None) } #[inline] - pub fn non_owning_reference(&'a self) -> u64 { + pub fn non_owning_reference(&self) -> u64 { self._tab.get::(Monster::VT_NON_OWNING_REFERENCE, Some(0)).unwrap() } #[inline] - pub fn vector_of_non_owning_references(&'a self) -> Option> { + pub fn vector_of_non_owning_references(&self) -> Option> { self._tab.get::>>(Monster::VT_VECTOR_OF_NON_OWNING_REFERENCES, None) } #[inline] @@ -1460,51 +1460,51 @@ impl<'a> TypeAliases<'a> { pub const VT_VF64: flatbuffers::VOffsetT = 26; #[inline] - pub fn i8_(&'a self) -> i8 { + pub fn i8_(&self) -> i8 { self._tab.get::(TypeAliases::VT_I8_, Some(0)).unwrap() } #[inline] - pub fn u8_(&'a self) -> u8 { + pub fn u8_(&self) -> u8 { self._tab.get::(TypeAliases::VT_U8_, Some(0)).unwrap() } #[inline] - pub fn i16_(&'a self) -> i16 { + pub fn i16_(&self) -> i16 { self._tab.get::(TypeAliases::VT_I16_, Some(0)).unwrap() } #[inline] - pub fn u16_(&'a self) -> u16 { + pub fn u16_(&self) -> u16 { self._tab.get::(TypeAliases::VT_U16_, Some(0)).unwrap() } #[inline] - pub fn i32_(&'a self) -> i32 { + pub fn i32_(&self) -> i32 { self._tab.get::(TypeAliases::VT_I32_, Some(0)).unwrap() } #[inline] - pub fn u32_(&'a self) -> u32 { + pub fn u32_(&self) -> u32 { self._tab.get::(TypeAliases::VT_U32_, Some(0)).unwrap() } #[inline] - pub fn i64_(&'a self) -> i64 { + pub fn i64_(&self) -> i64 { self._tab.get::(TypeAliases::VT_I64_, Some(0)).unwrap() } #[inline] - pub fn u64_(&'a self) -> u64 { + pub fn u64_(&self) -> u64 { self._tab.get::(TypeAliases::VT_U64_, Some(0)).unwrap() } #[inline] - pub fn f32_(&'a self) -> f32 { + pub fn f32_(&self) -> f32 { self._tab.get::(TypeAliases::VT_F32_, Some(0.0)).unwrap() } #[inline] - pub fn f64_(&'a self) -> f64 { + pub fn f64_(&self) -> f64 { self._tab.get::(TypeAliases::VT_F64_, Some(0.0)).unwrap() } #[inline] - pub fn v8(&'a self) -> Option<&'a [i8]> { + pub fn v8(&self) -> Option<&'a [i8]> { self._tab.get::>>(TypeAliases::VT_V8, None).map(|v| v.safe_slice()) } #[inline] - pub fn vf64(&'a self) -> Option> { + pub fn vf64(&self) -> Option> { self._tab.get::>>(TypeAliases::VT_VF64, None) } } diff --git a/tests/namespace_test/namespace_test1_generated.rs b/tests/namespace_test/namespace_test1_generated.rs index d12189170..42b12234e 100644 --- a/tests/namespace_test/namespace_test1_generated.rs +++ b/tests/namespace_test/namespace_test1_generated.rs @@ -184,7 +184,7 @@ impl<'a> TableInNestedNS<'a> { pub const VT_FOO: flatbuffers::VOffsetT = 4; #[inline] - pub fn foo(&'a self) -> i32 { + pub fn foo(&self) -> i32 { self._tab.get::(TableInNestedNS::VT_FOO, Some(0)).unwrap() } } diff --git a/tests/namespace_test/namespace_test2_generated.rs b/tests/namespace_test/namespace_test2_generated.rs index e420b899e..f748a08d2 100644 --- a/tests/namespace_test/namespace_test2_generated.rs +++ b/tests/namespace_test/namespace_test2_generated.rs @@ -55,15 +55,15 @@ impl<'a> TableInFirstNS<'a> { pub const VT_FOO_STRUCT: flatbuffers::VOffsetT = 8; #[inline] - pub fn foo_table(&'a self) -> Option> { + pub fn foo_table(&self) -> Option> { self._tab.get::>>(TableInFirstNS::VT_FOO_TABLE, None) } #[inline] - pub fn foo_enum(&'a self) -> namespace_b::EnumInNestedNS { + pub fn foo_enum(&self) -> namespace_b::EnumInNestedNS { self._tab.get::(TableInFirstNS::VT_FOO_ENUM, Some(namespace_b::EnumInNestedNS::A)).unwrap() } #[inline] - pub fn foo_struct(&'a self) -> Option<&'a namespace_b::StructInNestedNS> { + pub fn foo_struct(&self) -> Option<&'a namespace_b::StructInNestedNS> { self._tab.get::(TableInFirstNS::VT_FOO_STRUCT, None) } } @@ -151,7 +151,7 @@ impl<'a> SecondTableInA<'a> { pub const VT_REFER_TO_C: flatbuffers::VOffsetT = 4; #[inline] - pub fn refer_to_c(&'a self) -> Option> { + pub fn refer_to_c(&self) -> Option> { self._tab.get::>>(SecondTableInA::VT_REFER_TO_C, None) } } @@ -241,11 +241,11 @@ impl<'a> TableInC<'a> { pub const VT_REFER_TO_A2: flatbuffers::VOffsetT = 6; #[inline] - pub fn refer_to_a1(&'a self) -> Option> { + pub fn refer_to_a1(&self) -> Option> { self._tab.get::>>(TableInC::VT_REFER_TO_A1, None) } #[inline] - pub fn refer_to_a2(&'a self) -> Option> { + pub fn refer_to_a2(&self) -> Option> { self._tab.get::>>(TableInC::VT_REFER_TO_A2, None) } } diff --git a/tests/rust_usage_test/tests/integration_test.rs b/tests/rust_usage_test/tests/integration_test.rs index ca55f6527..39fe6eded 100644 --- a/tests/rust_usage_test/tests/integration_test.rs +++ b/tests/rust_usage_test/tests/integration_test.rs @@ -263,6 +263,23 @@ mod lifetime_correctness { // this line should compile: table.get::<&'static str>(0, None); } + + #[test] + fn table_object_self_lifetime_in_closure() { + // This test is designed to ensure that lifetimes for temporary intermediate tables aren't inflated beyond where the need to be. + let buf = load_file("../monsterdata_test.mon"); + let monster = my_game::example::get_root_as_monster(&buf); + let enemy: Option = monster.enemy(); + // This line won't compile if "self" is required to live for the lifetime of buf above as the borrow disappears at the end of the closure. + let enemy_of_my_enemy = enemy.map(|e| { + // enemy (the Option) is consumed, and the enum's value is taken as a temporary (e) at the start of the closure + let name = e.name(); + // ... the temporary dies here, so for this to compile name's lifetime must not be tied to the temporary + name + // If this test fails the error would be "`e` dropped here while still borrowed" + }); + assert_eq!(enemy_of_my_enemy, Some("Fred")); + } } #[cfg(test)]