正如标题所述,我希望从一个具有初始可变状态的函数返回一个闭包。在以下示例中,CowRow
是struct
,其中包含time
字段。它还有一个String
字段,因此不可复制。具体来说,我想要一个看起来像这样的函数:
pub fn agg1() -> Box<Fn(&CowRow)> {
let res = 0;
Box::new(move |r| { res += r.time; })
}
当然,这会产生错误:
src/queries.rs:9:25: 9:38 error: cannot assign to captured outer variable in an `Fn` closure
src/queries.rs:9 Box::new(move |r| { res += r.time; })
^~~~~~~~~~~~~
src/queries.rs:9:14: 9:41 help: consider changing this closure to take self by mutable reference
src/queries.rs:9 Box::new(move |r| { res += r.time; })
^~~~~~~~
我的理解是Rust需要知道返回值的大小,并且因为闭包从他们的环境中借用了堆栈帧,我们需要引入Box
和move
来获取大小。返回并将闭包放在堆上。
在这个闭包环境中是否有某种方法可以将res
放在堆上?或者以其他方式允许这种行为?我当然看过:Cannot borrow captured outer variable in an `Fn` closure as mutable但这看起来过于复杂,而且我不清楚在多个线程同时运行这个函数的情况下它会如何执行。
我尝试的另一种技术是更改闭包以对i32
进行可变引用,我可以在agg
函数之外进行初始化。例如:
pub fn agg0() -> Box<Fn(&CowRow, &mut i32)> {
Box::new(move |r, &mut acc| { acc += r.time; })
}
然而,这会产生错误:
src/queries.rs:4:35: 4:48 error: re-assignment of immutable variable `acc` [E0384]
src/queries.rs:4 Box::new(move |r, &mut acc| { acc += r.time; })
^~~~~~~~~~~~~
src/queries.rs:4:35: 4:48 help: run `rustc --explain E0384` to see a detailed explanation
src/queries.rs:4:28: 4:31 note: prior assignment occurs here
src/queries.rs:4 Box::new(move |r, &mut acc| { acc += r.time; })
这对我来说完全是个谜。
答案 0 :(得分:4)
你需要在这里做两件事:让Fn
变为可变,并返回一个Fn
闭包,而不是FnMut
闭包:
#-*-coding:utf8;-*-
#qpy:2
#qpy:kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from plyer import accelerometer
from kivy.clock import Clock
class UI(FloatLayout):
def __init__(self, **kwargs):
super(UI, self).__init__(**kwargs)
self.lblAcce = Label(text="Accelerometer: ") #create a label at the center
self.add_widget(self.lblAcce) #add the label at the screen
try:
accelerometer.enable() #enable the accelerometer
Clock.schedule_interval(self.update, 1.0/24) #24 calls per second
except:
self.lblAcce.text = "Fallo al iniciar el acelerometro" #error
def update(self, dt):
txt = ""
try:
txt = "Aceleracion: \nX = %.2f\nY = %.2f\nZ = %2.f " %(
accelerometer.acceleration[0], #read the X value
accelerometer.acceleration[1], # Y
accelerometer.acceleration[2]) # Z
except:
txt = "No se puede leer el acelerometro!" #error
self.lblAcce.text = txt #add the correct text
class TestApp(App):
def build(self):
# display a button with the text : Hello QPython
usi = UI()
return usi
if __name__== '__main__':
TestApp().run()
[app]
# (str) Title of your application
title = Acelerometro 1
# (str) Package name
package.name = acc1
# (str) Package domain (needed for android/ios packaging)
package.domain = org.test
# (str) Source code where the main.py live
source.dir = .
# (list) Source files to include (let empty to include all the files)
source.include_exts = py,png,jpg,kv,atlas
# (str) Application versioning (method 1)
version = 1.0
# (list) Application requirements
# comma seperated e.g. requirements = sqlite3,kivy
requirements = kivy, plyer
# (str) Supported orientation (one of landscape, portrait or all)
orientation = all
# (bool) Indicate if the application should be fullscreen or not
fullscreen = 1
[buildozer]
# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
log_level = 2
# (int) Display warning if buildozer is run as root (0 = False, 1 = True)
warn_on_root = 1
特征禁止实现者在调用时自行更改。由于此闭包正在修改自己的状态,这意味着它不能是android.permissions
[1]。相反,您需要使用 允许突变关闭的捕获环境的#portfolio {
height: 300px;
position: fixed;
z-index: 1;
background: #27f5eb;
top: 0;
width: 100%;
}
.mainContent {
z-index: 2;
position: relative;
z-index: 2;
height: 1000px;
background: purple;
margin-top: 300px;
}
h1 {
text-align: center;
}
body {margin: 0; padding: 0;}
。
[1]:当然,除非你涉及内部的可变性。
答案 1 :(得分:2)
DK.已经说过如何修复agg1
,但我想解释一下agg0
的错误,以确保完整性。
pub fn agg0() -> Box<Fn(&CowRow, &mut i32)> {
Box::new(move |r, &mut acc| { acc += r.time; })
}
我们可以从agg0
的返回类型推断闭包的第二个参数的类型是&mut i32
。 &mut acc
是一种解构可变引用的模式,将acc
定义为i32
,初始化为引用值的副本。你不能改变它,因为你没有定义acc
是可变的(你需要写&mut mut acc
而不是&mut acc
),但这不是你想要的,因为那时你会改变副本。你想要的是改变指向的整数,所以你需要像这样定义你的闭包:
pub fn agg0() -> Box<Fn(&CowRow, &mut i32)> {
Box::new(move |r, acc| { *acc += r.time; })
}
此处,acc
的类型为&mut i32
,因此为了改变i32
,我们需要首先取消引用指针(这会产生一个引用{{的左值) 1}}指针后面;它是不是副本!)。