I have the following generated code (from flatbuffers schema):
pub trait TFilterData {
fn get_text(&self) -> &str;
}
pub struct Table<'a> {
pub buf: &'a [u8],
pub loc: usize,
}
pub struct Data<'a> {
pub _tab: Table<'a>,
}
pub struct MatchResult<'a> {
pub _tab: Table<'a>,
}
pub struct FiltersByKeyword<'a> {
pub _tab: Table<'a>,
}
pub struct KeywordByFilter<'a> {
pub _tab: Table<'a>,
}
pub struct BlockingFilter<'a> {
pub _tab: Table<'a>,
}
impl TFilterData for BlockingFilter<'_> {
fn get_text(&self) -> &str {
todo!()
}
}
impl<'a> MatchResult<'a> {
pub fn blocking_filters(&self) -> Option<Vec<BlockingFilter<'a>>> {
None
}
}
impl<'a> FiltersByKeyword<'a> {
pub fn filters(&self) -> Option<Vec<MatchResult<'a>>> {
None
}
pub fn keyword(&self) -> String {
"".to_owned()
}
}
impl<'a> Data<'a> {
pub fn keyword_by_filter_list(&self) -> Option<Vec<KeywordByFilter<'a>>> {
None
}
pub fn filters_by_keyword_list(&self) -> Option<Vec<FiltersByKeyword<'a>>> {
None
}
}
I have my own struct that owns it:
pub type RcedFilterDataVec = Vec<Rc<dyn TFilterData>>;
pub struct FlatBuffersIndex<'a> {
data: Data<'a>,
filter_by_keyword: HashMap<String, RcedFilterDataVec>, // keyword -> list of `TFilterData`s
keyword_by_filter: HashMap<String, String>, // Filter -> keyword
}
impl<'a> FlatBuffersIndex<'a> {
pub fn new(buffer: &'a [u8]) -> Self {
let data: Data<'a> = Data {
_tab: Table {
buf: buffer,
loc: 0,
},
};
let mut keyword_by_filter = HashMap::new();
let mut filter_by_keyword = HashMap::new();
if let Some(some_filters_by_keyword_list) = data.filters_by_keyword_list() {
for key_value in some_filters_by_keyword_list {
let mut filters = RcedFilterDataVec::new();
// filters
for each_match_result in key_value.filters().unwrap() {
// TODO: there is no sense in mixing of them as later matcher will have to separate them
// blocking
if let Some(some_blocking_filters) = each_match_result.blocking_filters() {
for each_blocking_filter in some_blocking_filters {
// actually we don't need BlockingFilter part more than RegExpFilter + FilterType
// let filter = each_blocking_filter.regexp_filter().clone();
let rc_filter = Rc::new(each_blocking_filter);
filters.push(rc_filter);
// TODO: add it!!! currently commented due to unclear lifetime issue
}
}
// ...
}
filter_by_keyword.insert(key_value.keyword().to_owned(), filters);
}
}
FlatBuffersIndex {
data,
filter_by_keyword,
keyword_by_filter,
}
}
}
When trying to iterate on it i have the following error output:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/lib.rs:83:58
|
83 | if let Some(some_filters_by_keyword_list) = data.filters_by_keyword_list() {
| ^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 72:6...
--> src/lib.rs:72:6
|
72 | impl<'a> FlatBuffersIndex<'a> {
| ^^
note: ...so that the types are compatible
--> src/lib.rs:83:58
|
83 | if let Some(some_filters_by_keyword_list) = data.filters_by_keyword_list() {
| ^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `&Data<'_>`
found `&Data<'a>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
--> src/lib.rs:111:13
|
111 | filter_by_keyword,
| ^^^^^^^^^^^^^^^^^
= note: expected `HashMap<_, Vec<Rc<(dyn TFilterData + 'static)>>>`
found `HashMap<_, Vec<Rc<dyn TFilterData>>>`
Why isn't it clear for the compiler that it will live for 'a
that seems to be ok here? How can i fix it?
PS. This is the flatbuffers IDL to generate the code:
namespace com.eyeo.adblockplus.fb;
table AbpFilter {
filter_text: string (required); // Strings are UTF-8, [ubyte] might be needed. How will we have `String` without convertions then?
filter_type: ushort;
}
table CommentFilter {
filter: AbpFilter (required);
}
table InvalidFilter {
filter: AbpFilter (required);
reason: string (required);
}
table ActiveFilter {
filter: AbpFilter (required);
sitekeys: [string];
domain_separator: string (required);
domain_source: string;
domain_source_is_lower_case: bool;
}
table RegExpFilter {
active_filter: ActiveFilter (required);
third_party: bool;
content_type: uint;
pattern: string;
rewrite: string;
}
// Probably we don't really need subclasses of RegExpFilter for matching
// as we have RegExpFilter + FilterType to be used by Matcher when matching
table BlockingFilter {
regexp_filter: RegExpFilter (required);
collapse: bool; // probably unneeded
csp: string; // probably unneeded
}
table AllowingFilter {
regexp_filter: RegExpFilter (required);
}
table MatchResult {
blocking_filters: [BlockingFilter];
allowing_filters: [AllowingFilter];
}
// non-abstract filters (leafs)
// vector of union's is not supported unfortunately,
// thus we can't declare `filters: [FilterUnion]` in `struct` or `table`. Can we?
// union FilterUnion {
// CommentFilter,
// InvalidFilter,
// }
table KeywordByFilter {
filter_text: string (required, key);
keyword: string (required);
}
table FiltersByKeyword {
keyword: string (key);
filters: [MatchResult];
}
table Storage {
invalid_filters: [InvalidFilter];
comment_filters: [CommentFilter];
}
table Data {
filters: [MatchResult];
keyword_by_filter_list: [KeywordByFilter];
filters_by_keyword_list: [FiltersByKeyword];
storage: Storage;
}
root_type Data;
PPS. i've found it happens only when iterating over another vector from flatbuffers collection (each_match_result.blocking_filters()
or each_match_result.allowing_filters()
) and adding it to a new filters vector filters
that is put into filter_by_keyword
:
if let Some(some_blocking_filters) = each_match_result.blocking_filters() {
for each_blocking_filter in some_blocking_filters {
// actually we don't need BlockingFilter part more than RegExpFilter + FilterType
filters.push(Rc::new(each_blocking_filter.regexp_filter()));
}
}
Here is how this other collections is declared:
pub fn blocking_filters(&self) -> Option<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<BlockingFilter<'a>>>> {
self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<flatbuffers::ForwardsUOffset<BlockingFilter<'a>>>>>(MatchResult::VT_BLOCKING_FILTERS, None)
}