Skip to content

.into() for &str to IString conversion often leads to confusing and annoying errors #56

Closed
@kirillsemyonkin

Description

@kirillsemyonkin

Given the following function:

pub fn test(example: &str) -> IString {
    example.into()
}

The Rust complains about the following problem:

error[E0521]: borrowed data escapes outside of function
   |
18 | pub fn test(example: &str) -> IString {
   |             -------  - let's call the lifetime of this reference `'1`
   |             |
   |             `example` is a reference that is only valid in the function body
19 |     example.into()
   |     ^^^^^^^^^^^^^^
   |     |
   |     `example` escapes the function body here
   |     argument requires that `'1` must outlive `'static`

The &'1 str in question is asked to be 'static which is not obvious at all.

But the highlighted place is the into() call, which points the user where to look for to fix the problem. Here is a more evil example:

pub fn test(example: &str) -> IString {
    let (first, _) = example.split_once('.').unwrap();
    first.into()
}

This leads to the following error:

error[E0521]: borrowed data escapes outside of function
   |
18 | pub fn test(example: &str) -> IString {
   |             -------  - let's call the lifetime of this reference `'1`
   |             |
   |             `example` is a reference that is only valid in the function body
19 |     let (first, _) = example.split_once('.').unwrap();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^
   |                      |
   |                      `example` escapes the function body here
   |                      argument requires that `'1` must outlive `'static`

Now the highlighted line in question is example.split_once('.'). This is terrible for figuring out what is actually wrong with the code in question, because the problem is actually the following impl inside of IString's code and the corresponding into() call:

impl From<&'static str> for IString {
fn from(s: &'static str) -> IString {
IString::Static(s)
}
}

The fact that it uses &'static str makes Rust confused, as it does not talk about specific implementations and such, but rather talks about it as if it was a fundamental check inside the language, just because 'static is this fundamental. This makes figuring out what Rust actually wants me to fix in my code using ImplicitClone very tedious and slows down development.

The best solution would be specialization for 'a so that it is able to correctly perform Rc::from call inside its specific implementation. This, however, collides with the 'static implementation since 'a can be 'static as well, and I guess there must be a way for the string slice in question to be of indeterminate lifetime (as in, it can be either 'static or not, but we have to know precisely if it is, or else it is not possible to know which enum IString variant should be applied to it).

A possible solution could be to remove this From<&'static str> altogether, in favor of forcing users to perform IString::Static(...) instead.

A common way to overcome this problem is to write str.to_string().into(), but it might be not as efficient as doing Rc::from(str).into() (I have not tested the two approaches, but I would assume the intermediate String is heavier than Rc<str>, unless it is optimized out in a release build). Namely, the ImplicitClone codebase contains the following FromStr implementation (may require a separate issue/a PR that does not close this issue; there might be more places where such things happen):

Ok(IString::from(String::from(value)))

Brainstorming more solutions is welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions