How to deal with borrowed value does not live long enough for Arc in for loops

Hey, I am having an issue with borrowing an Arc in a for loop. I thought Arc had a shared ownership so I am confused on what the issue is. I read through other similar issues although most were solved by calling .to_string() to make a &str reference owned. Is there a way to do that with Arc? I tried to do a clone(), and I tried just changing input_element to store an optional reference to an element, but both did not work as the value did not live long enough. Could anyone help with this issue?
Here is my code:

struct MyInfoQuestions {
    question: String,
    input_element: Option<Element<'static>>,
    is_required: bool,
    input_type: Option<InputType>,
}
fn submit_my_info(tab: Arc<Tab>) -> StepFuture {
    Box::pin(async move {
        /* Set Up - Prep Time */
        let div_elem = match tab.wait_for_element("div[data-automation-id='applyFlowMyInfoPage']") {
            Ok(div_elem) => div_elem,
            Err(wait_err) => {
                return Err(HttpResponse::BadRequest().body(format!("Wait Error: {}", wait_err)));
            }
        };
        let labels = match div_elem.wait_for_elements("label") {
            Ok(labels) => labels,
            Err(wait_err) => {
                return Err(HttpResponse::BadRequest().body(format!("Wait Error: {}", wait_err)));
            }
        };
        // let mut is_radio = false;
        // let mut radio_class = "".to_string();
        let mut questions = Vec::new();
        for label in labels {
            let question_opt = label.get_inner_text().ok();
            let for_label = if let Ok(label) = label.get_attribute_value("for") {
                label
            } else {
                None
            };
            let question = if let Some(question) = question_opt {
                question
            } else {
                continue;
            };
            let is_required = match question.chars().last() {
                Some(last_char) => last_char == '*',
                None => false,
            };
            if let Some(label) = for_label {
                let input_elem: Element = match tab.find_element(format!("#{}", label).as_str()) {
                    Ok(elem) => elem,
                    Err(e) => {
                        return Err(HttpResponse::BadRequest()
                            .body(format!("Input For Label Error: {}", e)));
                    }
                };
                let input_type = match input_elem.get_attribute_value("type") {
                    Ok(inp_type) => inp_type,
                    Err(e) => {
                        return Err(HttpResponse::BadRequest()
                            .body(format!("Input Type Label Error: {}", e)));
                    }
                };
                match input_type {
                    Some(input_type) => match input_type.as_str() {
                        "text" => {
                            questions.push(MyInfoQuestions {
                                question,
                                input_element: Some(input_elem),
                                is_required,
                                input_type: Some(InputType::Text),
                            });
                        }
                        "radio" => {
                            questions.push(MyInfoQuestions {
                                question,
                                input_element: Some(input_elem),
                                is_required,
                                input_type: Some(InputType::Radio),
                            });
                        }
                        "checkbox" => {
                            questions.push(MyInfoQuestions {
                                question,
                                input_element: Some(input_elem),
                                is_required,
                                input_type: Some(InputType::CheckBox),
                            });
                        }
                        _ => {
                            questions.push(MyInfoQuestions {
                                question,
                                input_element: Some(input_elem),
                                is_required,
                                input_type: None,
                            });
                        }
                    },
                    None => {
                        questions.push(MyInfoQuestions {
                            question,
                            input_element: Some(input_elem),
                            is_required,
                            input_type: None,
                        });
                    }
                }
            }
        }
        // let question_and_input = match questions {
        //     Ok(question) => question,
        //     Err(err) => {
        //         return Err(err);
        //     }
        // };

        // if let Ok(Some(class)) = elem.get_attribute_value("class") {
        //     if is_radio {
        //         radio_class = class.clone();
        //         is_radio = false
        //     }
        //     if class != radio_class {
        //         radio_class = "".to_string();
        //     }
        // }
        // if radio_class != "".to_string() {
        //     return None;
        // }
        // if let Ok(Some(id)) = elem.get_attribute_value("id") {
        //     if id == "radio-label1" {
        //         is_radio = true;
        //     }
        // }

        /* Only Required */
        let required_inputs: Vec<_> = questions
            .into_iter()
            .filter_map(|info| {
                if info.is_required {
                    return Some(info.input_element);
                }
                None
            })
            .collect::<Vec<_>>();

        println!("First: {:?}", required_inputs[0]);
        // println!("Questions: {:?}", question_and_input);

        Ok(())
    })
}

Here is the error:

256 | fn submit_my_info(tab: Arc<Tab>) -> StepFuture {
    |                   --- binding `tab` declared here
...
291 |                 let input_elem: Element = match tab.find_element(format!("#{}", label).as_str()) {
    |                                                 ^^^ borrowed value does not live long enough
...
343 |                             input_element: Some(input_elem),
    |                                            ---------------- this usage requires that `tab` is borrowed for `'static`
...
392 |     })
    |     - `tab` dropped here while still borrowed

Thank you for any help on this.

Edit: Sorry, I figured out the issue. I had to change to:

struct MyInfoQuestions {
    question: String,
    input_element: Option<Element<'static>>,
    is_required: bool,
    input_type: Option<InputType>,
}

to

struct MyInfoQuestions<'a> {
    question: String,
    input_element: Option<Element<'a>>,
    is_required: bool,
    input_type: Option<InputType>,
}

I get why this solved it because the vector can infer lifetime. Is there a way to pass in another lifetime except for static?

you didn't show the definition of the type Tab, but guessing from the erro rmessage, it seems tab.find_element() returns a borrowed Element here;

but the definition of MyInfoQuestions requires input_element be 'static.

either you change MyInfoQuestions to remove the 'static so it accept a scoped borrow:

struct MyInfoQuestions<'a> {
    question: String,
    input_element: Option<Element<'a>>,
    is_required: bool,
    input_type: Option<InputType>,
}

if you need the 'static in MyInfoQuestions for some reason, then you'll have to change Tab::find_element() to return an owned Element (or, borrowed for 'static lifetime somehow).

2 Likes

Can you rephrase that sentence, it isn't clear. With your change, you are using another lifetime (not 'static).

1 Like

I'm not sure what you mean by "pass in" here, but I'll take a stab at the misunderstanding: Rust lifetimes are generally about the duration of borrows, and the borrow checker is a compile time pass-or-fail test -- it cannot change the semantics of a valid (compiling) program.

So, for example, annotating something with 'static can't make some value last longer before being destructed. Instead it is directive to the compiler to not accept the program if, generally speaking, some borrow cannot last forever.

What seems to be going on with your code is that that your questions are borrowing from the data that tab: Arc<Tab> owns. When your data type used 'static, you were in effect telling the compiler you needed to borrow from the tab forever. But that conflicts with the tab being destructed when the future is destructed, so the borrow checker complained.

Adding the lifetime parameter allowed the borrows to be shorter, and the conflict was resolved: you only actually need the borrows to last through the println.

3 Likes

tab is an object from the headless chrome rust library. Thank you for the insight

When I had it as

struct MyInfoQuestions {
    question: String,
    input_element: Option<Element<'static>>,
    is_required: bool,
    input_type: Option<InputType>,
}

Is there some other type of lifetime you can use like so?

struct MyInfoQuestions {
    question: String,
    input_element: Option<Element<'other_lifetime>>,
    is_required: bool,
    input_type: Option<InputType>,
}
1 Like

Thank you for the insight and clearing up the misunderstanding.

No. 'static is the only lifetime that can be used on a struct field without providing a lifetime parameter on the struct to provide a bound on the use of that struct by the caller.

1 Like