patternrustCritical
Does println! borrow or own the variable?
Viewed 0 times
ownprintlnthedoesvariableborrow
Problem
I am confused with borrowing and ownership. In the Rust documentation about reference and borrowing
They say
I am confused by this. If
I try to run this code below
This code is identical with the code above except I pass
let mut x = 5;
{
let y = &mut x;
*y += 1;
}
println!("{}", x);They say
println! can borrow x.I am confused by this. If
println! borrows x, why does it pass x not &x?I try to run this code below
fn main() {
let mut x = 5;
{
let y = &mut x;
*y += 1;
}
println!("{}", &x);
}This code is identical with the code above except I pass
&x to println!. It prints '6' to the console which is correct and is the same result as the first code.Solution
The macros
These macros do not behave as normal functions and macros do for reasons of convenience; the fact that they take references silently is part of that difference.
Run it through
Tidied further, it’s this:
Note the
If you write
Early 2023 update:
-
Since mid-2021, the required invocation has been
-
Since 2023-01-28 or so (https://github.com/rust-lang/rust/pull/106745),
print!, println!, eprint!, eprintln!, write!, writeln! and format! are a special case and implicitly take a reference to any arguments to be formatted.These macros do not behave as normal functions and macros do for reasons of convenience; the fact that they take references silently is part of that difference.
fn main() {
let x = 5;
println!("{}", x);
}Run it through
rustc -Z unstable-options --pretty expanded on the nightly compiler and we can see what println! expands to:#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
fn main() {
let x = 5;
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["", "\n"],
&match (&x,) {
(arg0,) => [::core::fmt::ArgumentV1::new(
arg0,
::core::fmt::Display::fmt,
)],
},
));
};
}Tidied further, it’s this:
use std::{fmt, io};
fn main() {
let x = 5;
io::_print(fmt::Arguments::new_v1(
&["", "\n"],
&[fmt::ArgumentV1::new(&x, fmt::Display::fmt)],
// ^^
));
}Note the
&x.If you write
println!("{}", &x), you are then dealing with two levels of references; this has the same result because there is an implementation of std::fmt::Display for &T where T implements Display (shown as impl Display for &'a T where T: Display + ?Sized) which just passes it through. You could just as well write &&&&&&&&&&&&&&&&&&&&&&&x.Early 2023 update:
-
Since mid-2021, the required invocation has been
rustc -Zunpretty=expanded rather than rustc -Zunstable-options --pretty=expanded.-
Since 2023-01-28 or so (https://github.com/rust-lang/rust/pull/106745),
format_args! is part of the AST, and so the expansion of println!("{}", x) is ::std::io::_print(format_args!("{0}\n", x));, not exposing the Arguments::new_v1 construction and &x aspects. This is good for various reasons (read #106745’s description), but ruins my clear demonstration here that x was only taken by reference. (This is why I’ve added this as a note at the end rather than updating the answer—since it no longer works.)Code Snippets
fn main() {
let x = 5;
println!("{}", x);
}#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
fn main() {
let x = 5;
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["", "\n"],
&match (&x,) {
(arg0,) => [::core::fmt::ArgumentV1::new(
arg0,
::core::fmt::Display::fmt,
)],
},
));
};
}use std::{fmt, io};
fn main() {
let x = 5;
io::_print(fmt::Arguments::new_v1(
&["", "\n"],
&[fmt::ArgumentV1::new(&x, fmt::Display::fmt)],
// ^^
));
}Context
Stack Overflow Q#30450399, score: 124
Revisions (0)
No revisions yet.