返回在Rust

时间:2016-04-08 23:57:43

标签: closures rust lifetime

正如标题所述,我希望从一个具有初始可变状态的函数返回一个闭包。在以下示例中,CowRowstruct,其中包含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需要知道返回值的大小,并且因为闭包从他们的环境中借用了堆栈帧,我们需要引入Boxmove来获取大小。返回并将闭包放在堆上。

在这个闭包环境中是否有某种方法可以将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; })

这对我来说完全是个谜。

2 个答案:

答案 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}}指针后面;它是不是副本!)。