Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Borrowed Ref seems to outlive its block #76601

Closed
snoyberg opened this issue Sep 11, 2020 · 3 comments
Closed

Borrowed Ref seems to outlive its block #76601

snoyberg opened this issue Sep 11, 2020 · 3 comments
Labels
C-bug Category: This is a bug.

Comments

@snoyberg
Copy link
Contributor

This may be a misunderstanding on my part, apologies for the noise if so. In the code below, I believe the Ref is improperly living beyond its containing block, and the code in question should be a compile-time error instead of a runtime panic. Consider this code:

use std::cell::RefCell;

fn main() {
    let strings: RefCell<Vec<String>> = RefCell::new(Vec::new());
    let _hello: &str = {
        strings.borrow_mut().push("Hello".to_string());
        &strings.borrow()[0]
    };
    strings.borrow_mut();
}

The strings.borrow() call results in a Ref<Vec<String>>, which is then indexed to get a &str which is the result of the block. Importantly, note that the &str is borrowed from the Ref, and therefore should not extend lifetime beyond the enclosing block. Therefore, from my understanding, this should be a compile time error.

Instead, the Ref appears to continue to live. The evidence of this is that this program compiles successfully, but when run, the borrow_mut() call after the block panics since there is still an active borrow on the RefCell. A variation on the program above helps clarify the situation further. If I explicitly capture the result of strings.borrow(), like so:

use std::cell::RefCell;

fn main() {
    let strings: RefCell<Vec<String>> = RefCell::new(Vec::new());
    let _hello: &str = {
        strings.borrow_mut().push("Hello".to_string());
        let strings_ref = strings.borrow();
        &strings_ref[0]
    };
    strings.borrow_mut();
}

I get the expected error message about strings_ref not living long enough:

error[E0597]: `strings_ref` does not live long enough
 --> src\main.rs:8:10
  |
5 |     let _hello: &str = {
  |         ------ borrow later stored here
...
8 |         &strings_ref[0]
  |          ^^^^^^^^^^^ borrowed value does not live long enough
9 |     };
  |     - `strings_ref` dropped here while still borrowed

Meta

I tested with both stable 1.46.0 release:

>rustc --version --verbose
rustc 1.46.0 (04488afe3 2020-08-24)
binary: rustc
commit-hash: 04488afe34512aa4c33566eb16d8c912a3ae04f9
commit-date: 2020-08-24
host: x86_64-pc-windows-msvc
release: 1.46.0
LLVM version: 10.0

And with nightly for 2020-09-11:

>rustc +nightly --version --verbose
rustc 1.48.0-nightly (a1947b3f9 2020-09-10)
binary: rustc
commit-hash: a1947b3f9e2831e2060bc42f0c78e4a2bb67930a
commit-date: 2020-09-10
host: x86_64-pc-windows-msvc
release: 1.48.0-nightly
LLVM version: 11.0

The panics are slightly different between the two versions. On stable, the panic references a code location with std, while nightly references a code location within my own project. Stable:

>cargo run
   Compiling foo v0.1.0 (C:\Users\snoyb\Desktop\foo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.50s
     Running `target\debug\foo.exe`
thread 'main' panicked at 'already borrowed: BorrowMutError', C:\Users\snoyb\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\src\libcore\cell.rs:867:31
stack backtrace:
   0: backtrace::backtrace::trace_unsynchronized
             at C:\Users\runneradmin\.cargo\registry\src\github.com-1ecc6299db9ec823\backtrace-0.3.46\src\backtrace\mod.rs:66
   1: std::sys_common::backtrace::_print_fmt
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\sys_common\backtrace.rs:78
   2: std::sys_common::backtrace::_print::{{impl}}::fmt
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\sys_common\backtrace.rs:59
   3: core::fmt::write
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libcore\fmt\mod.rs:1076
   4: std::io::Write::write_fmt<std::sys::windows::stdio::Stderr>
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\io\mod.rs:1537
   5: std::sys_common::backtrace::_print
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\sys_common\backtrace.rs:62
   6: std::sys_common::backtrace::print
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\sys_common\backtrace.rs:49
   7: std::panicking::default_hook::{{closure}}
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\panicking.rs:198
   8: std::panicking::default_hook
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\panicking.rs:217
   9: std::panicking::rust_panic_with_hook
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\panicking.rs:526
  10: std::panicking::begin_panic_handler
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\panicking.rs:437
  11: core::panicking::panic_fmt
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libcore\panicking.rs:85
  12: core::option::expect_none_failed
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libcore\option.rs:1269
  13: core::result::Result<core::cell::RefMut<alloc::vec::Vec<alloc::string::String>>, core::cell::BorrowMutError>::expect<core::cell::RefMut<alloc::vec::Vec<alloc::string::String>>,core::cell::BorrowMutError>
             at C:\Users\snoyb\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\src\libcore\result.rs:963
  14: core::cell::RefCell<alloc::vec::Vec<alloc::string::String>>::borrow_mut<alloc::vec::Vec<alloc::string::String>>
             at C:\Users\snoyb\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\src\libcore\cell.rs:867
  15: foo::main
             at .\src\main.rs:9
  16: std::rt::lang_start::{{closure}}<tuple<>>
             at C:\Users\snoyb\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\src\libstd\rt.rs:67
  17: std::rt::lang_start_internal::{{closure}}
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\rt.rs:52
  18: std::panicking::try::do_call
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\panicking.rs:348
  19: std::panicking::try
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\panicking.rs:325
  20: std::panic::catch_unwind
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\panic.rs:394
  21: std::rt::lang_start_internal
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\/src\libstd\rt.rs:51
  22: std::rt::lang_start<tuple<>>
             at C:\Users\snoyb\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\src\libstd\rt.rs:67
  23: main
  24: invoke_main
             at D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  25: __scrt_common_main_seh
             at D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
  26: BaseThreadInitThunk
  27: RtlUserThreadStart
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
error: process didn't exit successfully: `target\debug\foo.exe` (exit code: 101)

And nightly:

>cargo +nightly run
   Compiling foo v0.1.0 (C:\Users\snoyb\Desktop\foo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.46s
     Running `target\debug\foo.exe`
thread 'main' panicked at 'already borrowed: BorrowMutError', src\main.rs:9:13
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/a1947b3f9e2831e2060bc42f0c78e4a2bb67930a\/library\std\src\panicking.rs:483
   1: core::panicking::panic_fmt
             at /rustc/a1947b3f9e2831e2060bc42f0c78e4a2bb67930a\/library\core\src\panicking.rs:85
   2: core::option::expect_none_failed
             at /rustc/a1947b3f9e2831e2060bc42f0c78e4a2bb67930a\/library\core\src\option.rs:1221
   3: core::result::Result<core::cell::RefMut<alloc::vec::Vec<alloc::string::String>>, core::cell::BorrowMutError>::expect<core::cell::RefMut<alloc::vec::Vec<alloc::string::String>>,core::cell::BorrowMutError>
             at /rustc/a1947b3f9e2831e2060bc42f0c78e4a2bb67930a\library\core\src\result.rs:933
   4: core::cell::RefCell<alloc::vec::Vec<alloc::string::String>>::borrow_mut<alloc::vec::Vec<alloc::string::String>>
             at /rustc/a1947b3f9e2831e2060bc42f0c78e4a2bb67930a\library\core\src\cell.rs:869
   5: foo::main
             at .\src\main.rs:9
   6: core::ops::function::FnOnce::call_once<fn(),tuple<>>
             at /rustc/a1947b3f9e2831e2060bc42f0c78e4a2bb67930a\library\core\src\ops\function.rs:227
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
error: process didn't exit successfully: `target\debug\foo.exe` (exit code: 101)
@snoyberg snoyberg added the C-bug Category: This is a bug. label Sep 11, 2020
@jonas-schievink
Copy link
Contributor

Temporaries created in the trailing expression of a block can have their lifetime extended to cover the let bindings. This is documented here: https://doc.rust-lang.org/reference/destructors.html?highlight=temporary,life#temporary-lifetime-extension

@snoyberg
Copy link
Contributor Author

Wow, thank you for the very fast explanation! Sorry for the noise.

@jonas-schievink
Copy link
Contributor

No worries!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

2 participants