rust


Why does Rust store i64s captured by a closure as i64*s in the LLVM IR closure environment?


In this simple example
#[inline(never)]
fn apply<F, A, B>(f: F, x: A) -> B
where F: FnOnce(A) -> B {
f(x)
}
fn main() {
let y: i64 = 1;
let z: i64 = 2;
let f = |x: i64| x + y + z;
print!("{}", apply(f, 42));
}
the closure passed to apply is passed as a LLVM IR {i64*, i64*}*:
%closure = type { i64*, i64* }
define internal fastcc i64 #apply(%closure* noalias nocapture readonly dereferenceable(16)) unnamed_addr #0 personality i32 (i32, i32, i64, %"8.unwind::libunwind::_Unwind_Exception"*, %"8.unwind::libunwind::_Unwind_Context"*)* #rust_eh_personality {
entry-block:
%1 = getelementptr inbounds %closure, %closure* %0, i64 0, i32 1
%2 = getelementptr inbounds %closure, %closure* %0, i64 0, i32 0
%3 = load i64*, i64** %2, align 8
%4 = load i64*, i64** %1, align 8
%.idx.val.val.i = load i64, i64* %3, align 8, !noalias !1
%.idx1.val.val.i = load i64, i64* %4, align 8, !noalias !1
%5 = add i64 %.idx.val.val.i, 42
%6 = add i64 %5, %.idx1.val.val.i
ret i64 %6
}
(apply actually has a more complicated name in the generated LLVM code.)
This causes two loads to get to each of the captured variables. Why isn't %closure just {i64, i64} (which would make the argument to apply {i64, i64}*)?
Closures capture by reference by default. You can change that behavior to capture by value by adding the move keyword before the parameter list:
let f = move |x: i64| x + y + z;
This generates much leaner code:
define internal fastcc i64 #apply(i64 %.0.0.val, i64 %.0.1.val) unnamed_addr #0 personality i32 (i32, i32, i64, %"8.unwind::libunwind::_Unwind_Exception"*, %"8.unwind::libunwind::_Unwind_Context"*)* #rust_eh_personality {
entry-block:
%0 = add i64 %.0.0.val, 42
%1 = add i64 %0, %.0.1.val
ret i64 %1
}
Adding the move keyword means that any value that the closure uses will be moved into the closure's environment. In the case of integers, which are Copy, it doesn't make much difference, but in the case of other types like String, it means that you can't use the String anymore in the outer scope after creating the closure. It's an all-or-nothing deal, but you can manually take references to individual variables outside a move closure and have the closure use these references instead of the original values to get manual capture-by-reference behavior.
Can you observe the value vs ref difference somehow in this code?
If you take the address of the captured variable, you can observe the difference. Notice how the first and second output lines are the same, and the third is different.

Related Links

How to use 'on_send' method of Nickel response?
How to build an executable that depends on curl for x86_64-unknown-linux-musl
How can I create a struct with a HashMap where the keys the same type as the container
Issues constraining implementation lifetimes on type without lifetime parameter
How to use recursive associated types in functions?
Right way to share a reference between closures
Borrow checker and function arguments in Rust, correct or over zealous?
Most idiomatic way to create a default struct
How to return an Iterator from a Map [duplicate]
Do all primitive types implement the Copy trait?
Comparison of two floats in Rust to arbitrary level of precision
Is there a convenient way to represent x86 instructions in a struct or other language feature?
Why isn't `regex!` a wrapper for `Regex::new` to offer the same regex matching speed?
Lifetime annotation for closure argument
“overflow while adding drop-check rules” while implementing a fingertree
Is it possible to coerce slices/vectors into 'Cow<[type]>' in function arguments?

Categories

HOME
entity-framework
list
keyboard
blast
vagrant
yahoo-weather-api
devexpress
subdomain
gallery
angular-meteor
mailing-list
selection
clickable-image
siesta
jpa-2.0
tomcat8
android-custom-view
opam
pe
maven-surefire-plugin
gollum-wiki
qa
channel
code-climate
bonita
sparkle
splunk-query
gnu-classpath
adminer
restful-architecture
forms-authentication
task-parallel-library
classnotfoundexception
hypothesis-test
latexml
rightnow-crm
section508
directwrite
rm
glassfish-4.1
kendo-editor
cdo.message
timestamping
mathnet
gulp-uglify
teamviewer
flipclock
grinder
phpquery
dnx50
enaml
postal-code
replicaset
artisan
uno
utf8-decode
cout
smacss
zend-mail
igor
full-text-indexing
forceclose
miglayout
bittorrent-sync
map
application-blocks
coalesce
xs
blobstorage
donut-chart
semantic-merge
multiple-dispatch
stretch
system-information
gamesalad
hardcode
mkannotation
sigar
zend-server-ce
litespeed
firefly-mv
qt-mobility
kext
activestate
yahoo-maps
source-code-protection
external-sorting
downcasting
ntruencrypt
gui-designer

Resources

Encrypt Message