rust python_用于Python程序员的Rust

rust python

Now that Rust 1.0 is out and quite stable, I thought it might be interesting to write an introduction to Rust for Python programmers. This guide goes over the basics of the language and compares different constructs and how they behave.

现在Rust 1.0已经发布并且相当稳定,我认为为Python程序员撰写Rust简介可能会很有趣。 本指南介绍了该语言的基础知识,并比较了不同的结构及其行为方式。

Rust language wise is a completely different beast compared to Python. Not just because one is a compiled language and the other one is interpreted, but also because the principles that go into them are completely different. However as different as the languages might be at the core, they share a lot in regards to ideas for how APIs should work. As a Python programmer a lot of concepts should feel very familiar.

与Python相比,Rust语言明智的做法完全不同。 不仅因为一种语言是编译语言,而且另一种语言是解释型语言,还因为其中所包含的原理完全不同。 但是,尽管核心语言可能有所不同,但它们在API应该如何工作方面的想法却有着很多共同点。 作为Python程序员,许多概念应该感到非常熟悉。

句法 (Syntax)

The first difference you will notice as a Python programmer is the syntax. Unlike Python, Rust is a language with lots of curly braces. However this is for a good reason and that is that Rust has anonymous functions, closures and lots of chaining that Python cannot support well. These features are much easier to understand and write in a non indentation based language. Let’s look at the same example in both languages.

作为Python程序员,您会注意到的第一个区别是语法。 与Python不同,Rust是一种带有大括号的语言。 但这是有充分的理由的,这就是Rust具有匿名函数,闭包和许多Python无法很好支持的链接的原因。 使用基于非缩进的语言更容易理解和编写这些功能。 让我们看看两种语言的相同示例。

First a Python example of printing “Hello World” three times:

首先是一个Python示例,它打印了三遍“ Hello World”:

def def mainmain ():
    ():
    for for count count in in rangerange (( 33 ):
        ):
        print print "{}. Hello World!""{}. Hello World!" .. formatformat (( countcount )
)

And here is the same in Rust:

这与Rust相同:

As you can see, quite similar. def becomes fn and colons become braces. The other big difference syntax wise is that Rust requires type information for parameters to function which is not something you do in Python. In Python 3 type annotations are available which share the same syntax as in Rust.

如您所见,非常相似。 def变成fn ,冒号变成花括号。 另一个明智的语法差异是Rust需要使用类型信息来使参数起作用,而这不是您在Python中要做的。 在Python 3中,可以使用类型注释,它们与Rust共享相同的语法。

One new concept compared to Python are these functions with exclamation marks at the end. Those are macros. A macro expands at compile time into something else. This for instance is used for string formatting and printing because this way the compiler can enforce correct format strings at compile time. It does not accidentally happen that you mismatch the types or number of arguments to a print function.

与Python相比,一个新概念是这些函数最后带有感叹号。 这些是宏。 宏在编译时扩展为其他内容。 例如,它用于字符串格式化和打印,因为这样编译器可以在编译时强制使用正确的格式字符串。 您不会意外地使打印函数的参数类型或数量不匹配。

特性与方案 (Traits vs Protocols)

The most familiar yet different feature is object behavior. In Python a class can opt into certain behavior by implementing special methods. This is usually called “conforming to a protocol”. For instance to make an object iterable it implements the __iter__ method that returns an iterator. These methods must be implemented in the class itself and cannot really be changed afterwards (ignoring monkeypatching).

最熟悉但又不同的功能是对象行为。 在Python中,类可以通过实现特殊方法来选择某些行为。 这通常被称为“符合协议”。 例如,为了使对象可迭代,它实现了返回迭代器的__iter__方法。 这些方法必须在类本身中实现,并且之后不能真正更改(忽略Monkeypatching)。

In Rust the concept is quite similar but instead of special methods, it uses traits. Traits are a bit different in that they accomplish the same goal but the implementation is locally scoped and you can implement more traits for types from another module. For instance if you want to give integers a special behavior you can do that without having to change anything about the integer type.

在Rust中,这个概念非常相似,但是它使用特征来代替特殊方法。 特性有些不同,它们可以实现相同的目标,但是实现是局部范围的,您可以从另一个模块中实现更多类型的特性。 例如,如果您想给整数一个特殊的行为,则可以这样做而不必更改有关整数类型的任何内容。

To compare this concept let’s see how to implement a type that can be added to itself. First in Python:

为了比较这个概念,让我们看看如何实现可以添加到自身的类型。 首先在Python中:

class class MyTypeMyType (( objectobject ):

    ):

    def def __init____init__ (( selfself , , valuevalue ):
        ):
        selfself .. value value = = value

    value

    def def __add____add__ (( selfself , , otherother ):
        ):
        if if not not isinstanceisinstance (( otherother , , MyTypeMyType ):
            ):
            return return NotImplemented
        NotImplemented
        return return selfself .. __class____class__ (( selfself .. value value + + otherother .. valuevalue )
)

And here is the same in Rust:

这与Rust相同:

Here the Rust example looks a bit longer but it also comes with automatic type handling which the Python example does not do. The first thing you notice is that in Python the methods live on the class, whereas in Rust the data and the operations live independently. The struct defines the data layout and the impl MyType define methods the type itself has, whereas impl Add for MyType implements the Add trait for that type. For the Add implementation we also need to define the result type of our add operations, but we avoid the extra complexity of having to check the type at runtime like we have to do in Python.

这里的Rust示例看起来更长一些,但是它还带有自动类型处理功能,而Python示例却没有。 您注意到的第一件事是,在Python中,方法存在于类中,而在Rust中,数据和操作则独立存在。 该结构定义数据布局,而impl MyType定义该类型本身具有的方法,而impl Add for MyType实现该类型的Add特性。 对于Add实现,我们还需要定义add操作的结果类型,但是避免了像在Python中那样必须在运行时检查类型的额外复杂性。

Another difference is that in Rust the constructor is explicit whereas in Python it’s quite magical. When you create an instance of an object in Python it will eventually call __init__ to initialize the object, whereas in Rust you just define a static method (by convention called new) which allocates and constructs the object.

另一个区别是,在Rust中,构造函数是显式的,而在Python中,它是非常神奇的。 当您在Python中创建对象的实例时,它将最终调用__init__来初始化该对象,而在Rust中,您只需定义一个静态方法(按照惯例称为new )来分配和构造该对象。

错误处理 (Error Handling)

Error handling in Python and Rust is completely different. Whereas in Python errors are thrown as exceptions, errors in Rust are passed back in the return value. This might sound strange at first but it’s actually a very nice concept. It’s pretty clear from looking at a function what error it returns.

Python和Rust中的错误处理完全不同。 而在Python中,错误会作为异常抛出,而Rust中的错误会在返回值中传回。 一开始听起来可能很奇怪,但是实际上这是一个非常好的概念。 通过查看函数可以很清楚地看到它返回什么错误。

This works because a function in Rust can return a Result. A Result is a parametrized type which has two sides: a success and a failure side. For instance Result means that the function either returns a 32bit integer in the success case or MyError if an error happens. What happens if you need to return more than one error? This is where things differ from a philosophical point of view.

之所以可行,是因为Rust中的函数可以返回Result结果是参数化类型,具有两个方面:成功和失败方面。 例如, 结果Result 表示该函数在成功的情况下返回32位整数,或者在发生错误时返回MyError 。 如果您需要返回多个错误,该怎么办? 这是从哲学的角度来看事物有所不同的地方。

In Python a function can fail with any error and there is nothing you can do about that. If you ever used the Python “requests” library and you caught down all request exceptions and then got annoyed that SSL errors are not caught by this, you will understand the problem. There is very little you can do if a library does not document what it returns.

在Python中,函数可能会因任何错误而失败,而您对此无能为力。 如果您曾经使用过Python“ requests”库,并且捕获了所有请求异常,然后由于没有捕获到SSL错误而感到恼火,那么您将理解问题所在。 如果库没有记录返回的内容,您将无能为力。

In Rust the situation is very different. A function signature includes the error. If you need to return two errors then the way to do this is to make a custom error type and to convert internal errors into a better one. For instance if you have an HTTP library and internally it might fail with Unicode errors, IO errors, SSL errors, what have you, you need to convert these errors into one error type specific to your library and users then only need to deal with that. Rust provides error chaining that such an error can still point back to the original error that created it if you need to.

在Rust中,情况大不相同。 功能签名包括错误。 如果您需要返回两个错误,那么执行此操作的方法是制作一个自定义错误类型并将内部错误转换为更好的错误类型。 例如,如果您有一个HTTP库,并且内部可能会因Unicode错误,IO错误,SSL错误而失败,那么您需要将这些错误转换为特定于您的库和用户的错误类型,然后只需要处理该错误即可。 。 Rust提供了错误链接,如果需要的话,这样的错误仍然可以指向创建它的原始错误。

You can also at any point use the Box type which any error converts into, if you are too lazy to make your own custom error type.

如果您懒得自己创建自己的自定义错误类型,则还可以随时使用Box 类型,将任何错误转换为该类型。

Where errors propagate invisibly in Python, errors propagate visibly in Rust. What this means is that you can see whenever a function returns an error even if you chose to not handle it there. This is enabled by the try! macro. This example demonstrates this:

在Python中看不见错误传播的地方,在Rust中看不见错误传播的地方。 这意味着即使函数选择不在那里处理错误,只要函数返回错误,您都可以看到。 试试看启用 宏。 此示例说明了这一点:

useuse    stdstd :::: fsfs :::: FileFile ;; 



fnfn    read_fileread_file (( pathpath ::    && PathPath ))    ->->    ResultResult << StringString ,,    ioio :::: ErrorError >>    {{ 

         letlet    mutmut    ff    ==    trytry !! (( FileFile :::: openopen (( pathpath ));)); 

         letlet    mutmut    rvrv    ==    StringString :::: newnew ();(); 

         trytry !! (( ff .. read_to_stringread_to_string (( && mutmut    rvrv ));)); 

         OkOk (( rvrv )) 

}} 

Both File::open and read_to_string can fail with an IO error. The try! macro will propagate the error upwards and cause an early return from the function and unpack the success side. When returning the result it needs to be wrapped in either Ok to indicate success or Err to indicate failure.

File :: openread_to_string都可能因IO错误而失败。 试试看! 宏会向上传播错误,并导致该函数尽早返回并解压成功端。 返回结果时,需要将其包装在“ 确定”中以表示成功,或在“ 错误”中表示失败。

The try! macro invokes the From trait to allow conversion of errors. For instance you could change the return value from io::Error to MyError and implement a conversion from io::Error to MyError by implementing the From trait and it would be automatically invoked there.

试试看! 宏调用From特性,以允许转换错误。 例如,您可以将返回值从io :: Error更改为MyError,并通过实现From trait实现从io :: ErrorMyError的转换,它将在此处自动调用。

Alternatively you can change the return value from io::Error to Box and any error can be returned. This way however you can only reason about errors at runtime and no longer at compile time.

或者,您可以将返回值从io :: Error更改为Box 并且可以返回任何错误。 但是,通过这种方式,您只能在运行时推理错误,而在编译时不再推理。

If you don’t want to handle an error and abort the execution instead, you can unwrap() a result. That way you get the success value and if the result was an error, then the program aborts.

如果您不想处理错误并中止执行,则可以unwrap()结果。 这样,您将获得成功值,如果结果为错误,则程序将中止。

可变性和所有权 (Mutability and Ownership)

The part where Rust and Python become completely different languages is the concept of mutability and ownership. Python is a garbage collected language and as a result pretty much everything can happen with the objects at runtime. You can freely pass them around and it will “just work”. Obviously you can still generate memory leaks but most problems will be resolved for you automatically at runtime.

Rust和Python成为完全不同的语言的部分是可变性和所有权的概念。 Python是一种垃圾收集语言,因此对象在运行时几乎可以发生所有事情。 您可以随意传递它们,它将“起作用”。 显然,您仍然可以生成内存泄漏,但是大多数问题将在运行时自动为您解决。

In Rust however there is no garbage collector, yet the memory management still works automatically. This is enabled by a concept known as ownership tracking. All things you can create are owned by another thing. If you want to compare this to Python you could imagine that all objects in Python are owned by the interpreter. In Rust ownership is much more local. Function calls can have a list of objects in which case the objects are owned by the list and the list is owned by the function’s scope.

但是在Rust中,没有垃圾收集器,但是内存管理仍然可以自动工作。 这由称为所有权跟踪的概念启用。 您可以创建的所有事物都归另一事物所有。 如果要将其与Python进行比较,可以想象Python中的所有对象都由解释器拥有。 在Rust中,所有权更为本地化。 函数调用可以具有对象列表,在这种情况下,对象归列表所有,而列表归函数范围保护。

More complex ownership scenarios can be expressed by lifetime annotations and the function signatures. For instance in the case of the Add implementation in the previous example the receiver was called self like in Python. However unlike in Python the value is “moved” into the function whereas in Python the method is invoked with a mutable reference. What this means is that in Python you could do something like this:

生命周期注释和功能签名可以表示更复杂的所有权方案。 例如,在上一个示例的Add实现中,接收方被称为self,就像在Python中一样。 但是,与Python中不同的是,该值“移动”到了函数中,而在Python中,该方法是通过可变引用来调用的。 这意味着在Python中您可以执行以下操作:

Whenever you add an instance of MyType to another object you also leak out self to a global list. That means if you run the above example you have two references to the first instance of MyType: one is in leaks the other is in a. In Rust this is impossible. There can only ever be one owner. If you would append self to leaks the compiler would “move” the value there and you could not return it from the function because it was already moved elsewhere. You would have to move it back first to return it (for instance by removing it from the list again).

每当将MyType的实例添加到另一个对象时,也会将自身泄漏到全局列表中。 这意味着,如果运行上面的示例,则有两个对MyType的第一个实例的引用:一个在泄漏中 ,另一个在a中 。 在Rust中,这是不可能的。 只能有一个所有者。 如果您将self附加到泄漏处,则编译器将在其中“移动”值,并且您无法从函数中返回它,因为它已被移至其他位置。 您必须先将其移回以返回它(例如,再次从列表中将其删除)。

So what do you do if you need to have two references to an object? You can borrow the value. You can have an unlimited number of immutable borrows but you can only ever have one mutable borrow (and only if no immutable borrows were given out).

那么,如果需要对一个对象有两个引用,该怎么办? 您可以借入值。 您可以拥有无​​限数量的不可变借项,但是您永远只能拥有一个可变借项(并且只有在没有提供不可变借项的情况下)。

Functions that operate on immutable borrows are marked as &self and functions that need a mutable borrow are marked as &mut self. You can only loan out references if you are the owner. If you want to move the value out of the function (for instance by returning it) you cannot have any outstanding loans and you cannot loan out values after having moved ownership away from yourself.

对不可变借位进行操作的函数被标记为&self ,需要可变借位的函数被标记为&mut self 。 如果您是所有者,则只能借出参考文献。 如果要将值从函数中移出(例如,通过返回值),则不能有任何未偿还的贷款,并且在将所有权从自己身上移开后也不能借出值。

This is a big change in how you think about programs but you will get used to it.

这是您对程序的看法的重大变化,但是您会习惯它。

运行时借用和可变所有者 (Runtime Borrows and Mutible Owners)

So far pretty much all this ownership tracking was verified at compile time. But what if you cannot verify ownership at compile time? There you have multiple options to your disposal. One example is that you can use a mutex. A mutex allows you to guarantee at runtime that only one person has a mutable borrow to an object but the mutex itself owns the object. That way you can write code that access the same object but only ever once thread can access it at the time.

到目前为止,几乎所有这些所有权跟踪都在编译时进行了验证。 但是,如果您无法在编译时验证所有权怎么办? 在那里您可以选择多种选择。 一个示例是您可以使用互斥锁。 互斥锁允许您在运行时保证只有一个人可以对对象进行可变借用,但互斥锁本身拥有该对象。 这样,您可以编写访问同一对象的代码,但是一次只有一次线程可以访问它。

As a result of this this also means that you cannot accidentally forget to use a mutex and cause a data race. It would not compile.

因此,这也意味着您不能意外忘记使用互斥锁并引起数据争用。 它不会编译。

But what if you want to program like in Python and you can’t find an owner for memory? In that case you can put an object into a referenced counted wrapper and loan it out at runtime this way. That way you get very close to Python behavior just that you can cause cycles. Python breaks up cycles in it’s garbage collector, Rust does not have an equivalent.

但是,如果您想像在Python中那样编程,而又找不到内存所有者,该怎么办? 在这种情况下,您可以将对象放入引用的计数包装器中,并以这种方式在运行时借出它。 这样一来,您会非常接近Python行为,只是会导致循环。 Python在其垃圾收集器中破坏了循环,Rust没有等效的循环。

To show this in a better way, let’s go with a complex Python example and the Rust equivalent:

为了更好地说明这一点,让我们来看一个复杂的Python示例和Rust等效的示例:

from from threading threading import import LockLock , , Thread

Thread

def def fibfib (( numnum ):
    ):
    if if num num < < 22 :
        :
        return return 1
    1
    return return fibfib (( num num - - 22 ) ) + + fibfib (( num num - - 11 )

)

def def thread_progthread_prog (( mutexmutex , , resultsresults , , ii ):
    ):
    rv rv = = fibfib (( ii )
    )
    with with mutexmutex :
        :
        resultsresults [[ ii ] ] = = rv

rv

def def mainmain ():
    ():
    mutex mutex = = LockLock ()
    ()
    results results = = {}

    {}

    threads threads = = []
    []
    for for i i in in xrangexrange (( 3535 ):
        ):
        thread thread = = ThreadThread (( targettarget == thread_progthread_prog , , argsargs == (( mutexmutex , , resultsresults , , ii ))
        ))
        threadsthreads .. appendappend (( threadthread )
        )
        threadthread .. startstart ()

    ()

    for for thread thread in in threadsthreads :
        :
        threadthread .. joinjoin ()

    ()

    for for ii , , rv rv in in sortedsorted (( resultsresults .. itemsitems ()):
        ()):
        print print "fib({}) = {}""fib({}) = {}" .. formatformat (( ii , , rvrv )
)

So what we do here is spawn 35 threads and make them compute in a very terrible manner increasing Fibonacci numbers. Then we join the threads and print the sorted results. One thing you immediately notice here is that there is no intrinsic relationship between the mutex (the lock) and the results array.

因此,我们在这里执行的是生成35个线程,并使它们以非常糟糕的方式进行计算,从而增加斐波那契数。 然后,我们加入线程并打印排序的结果。 您在此处立即注意到的一件事是,互斥锁(锁)与结果数组之间没有固有关系。

Here is the Rust example:

这是Rust示例:

The big differences to the Python version here is that we use a B-tree map instead of a hash table and we put that into an Arc’ed mutex. What’s that? First of all we use a B-tree because it sorts automatically which is what we want here. Then we put it into a mutex so that we can at runtime lock it. Relationship established. Lastly we put it into an Arc. An Arc reference counts what it encloses. In this case the mutex. This means that we can make sure the mutex gets deleted only after the last thread finished running. Neat.

与此处的Python版本的最大区别在于,我们使用B树映射而不是哈希表,并将其放入Arc'ed互斥锁中。 那是什么? 首先,我们使用B树,因为它会自动排序,这就是我们想要的。 然后,将其放入互斥锁中,以便在运行时锁定它。 关系建立。 最后,我们将其放入弧形。 弧参考会计算其包围的内容。 在这种情况下,互斥体。 这意味着我们可以确保仅在最后一个线程运行完毕后才删除互斥体。 整齐。

So here is how the code works: we count to 20 like in Python, and for each of those numbers we run a local function. Unlike in Python we can use a closure here. Then we make a copy of the Arc into the local thread. This means that each thread sees it’s own version of the Arc (internally this will increment the refcount and decrement automatically when the thread dies). Then we spawn the thread with a local function. The move tells us to move the closure into the thread. Then we run the Fibonacci function in each thread. When we lock our Arc we get back a result we can unwrap and the insert into. Ignore the unwrap for a moment, that’s just how you convert explicit results into panics. However the point is that you can only ever get the result map when you unlock the mutex. You cannot accidentally forget to lock!

这就是代码的工作原理:就像Python一样,我们的计数为20,对于每个数字,我们运行一个局部函数。 与Python不同,我们可以在此处使用闭包。 然后,我们将Arc的副本复制到本地线程中。 这意味着每个线程看到的都是自己的Arc版本(在内部,这将在线程死后自动增加引用计数并自动减少)。 然后,我们使用局部函数生成线程。 此举告诉我们将闭包移动到线程中。 然后,我们在每个线程中运行Fibonacci函数。 当我们锁定圆弧时,我们会得到一个结果,我们可以将其展开并插入。 暂时忽略包,这就是将显式结果转换为恐慌的方法。 但是,要点是,只有在解锁互斥锁后才能获得结果图。 您不会意外忘记锁定!

Then we collect all threads into a vector. Lastly we iterate over all threads, join them and then print the results.

然后,我们将所有线程收集到一个向量中。 最后,我们遍历所有线程,将它们加入,然后打印结果。

Two things of note here: there are very few visible types. Sure, there is the Arc and the Fibonacci function takes unsigned 64bit integers, but other than that, no types are visible. We can also use the B-tree map here instead of a hashtable because Rust provides us with such a type.

这里需要注意的两件事:可见类型很少。 当然,这里有Arc ,而Fibonacci函数采用无符号的64位整数,但是除此之外,没有类型可见。 我们也可以在这里使用B树映射而不是哈希表,因为Rust为我们提供了这种类型。

Iteration works exactly the same as in Python. The only difference there is that in Rust in this case we need to acquire the mutex because the compiler cannot know that the threads finished running and the mutex is not necessary. However there is an API that does not require this, it’s just not stable yet in Rust 1.0.

迭代的工作原理与Python完全相同。 唯一的区别是,在这种情况下,在Rust中我们需要获取互斥锁,因为编译器无法知道线程已完成运行并且不需要互斥锁。 但是,有一个API不需要这样做,它在Rust 1.0中还不稳定。

Performance wise pretty much what you expect would happen. (This example is intentionally terrible just to show how the threading works.)

性能明智的几乎是您所期望的。 (此示例故意显示线程是如何工作的。)

统一码 (Unicode)

My favorite topic: Unicode This is where Rust and Python differ quite a bit. Python (both 2 and 3) have a very similar Unicode model which is to map Unicode data against arrays of characters. In Rust however Unicode strings are always stored as UTF-8. I have covered this in the past about why this is a much better solution than what Python or C# are doing (see also UCS vs UTF-8 as Internal String Encoding). What’s however very interesting about Rust is how it deals with the ugly reality of our encoding world.

我最喜欢的主题:UnicodeRust和Python的差异很大。 Python(2和3)都具有非常相似的Unicode模型,该模型将Unicode数据映射到字符数组。 但是在Rust中,Unicode字符串始终存储为UTF-8。 过去,我已经谈到了为什么这是比Python或C#更好的解决方案(另请参见UCS vs UTF-8作为内部字符串编码 )。 然而,Rust非常有趣的是它如何处理我们的编码世界的丑陋现实。

The first thing is that Rust is perfectly aware that operating system APIs (both in Windows Unicode and Linux non-Unicode land) are pretty terrible. Unlike Python however it does not try to force Unicode into these areas, instead it has different string types that can (within reason) convert between each other reasonably cheap. This works very well in practice and makes string operations very fast.

第一件事是Rust完全意识到操作系统API(在Windows Unicode和Linux非Unicode领域都非常糟糕)。 与Python不同的是,它没有尝试将Unicode强制进入这些区域,而是具有不同的字符串类型,这些字符串类型可以(合理地)在彼此之间进行廉价转换。 这在实践中效果很好,并且使字符串操作非常快。

For the vast majority of programs there is no encoding/decoding necessary because they accept UTF-8, just need to run a cheap validation check, process on UTF-8 strings and then don’t need an encode on the way out. If they need to integrate with Windows Unicode APIs they internally use the WTF-8 encoding which quite cheaply can convert to UCS2 like UTF-16 and back.

对于绝大多数程序而言,因为它们接受UTF-8,所以不需要编码/解码,只需要运行便宜的验证检查,对UTF-8字符串进行处理,然后在输出时不需要编码。 如果他们需要与Windows Unicode API集成,则可以在内部使用WTF-8编码 ,该编码可以很便宜地转换为UCS2(如UTF-16)并返回。

At any point can you convert between Unicode and bytes and munch with the bytes as you need. Then you can later run a validation step and ensure that everything went as intended. This makes writing protocols both really fast and really convenient. Compared this to the constant encoding and decoding you have to deal with in Python just to support O(1) string indexing.

您可以随时在Unicode和字节之间进行转换,并根据需要对字节进行修改。 然后,您可以稍后运行验证步骤,并确保一切都按预期进行。 这使得编写协议既非常快速又非常方便。 与此相比,您只需要支持O(1)字符串索引就可以在Python中处理常量编码和解码。

Aside from a really good storage model for Unicode it also has lots of APIs for dealing with Unicode. Either as part of the language or on the excellent crates.io index. This includes case folding, categorization, Unicode regular expressions, Unicode normalization, well conforming URI/IRI/URL APIs, segmentation or just simple things as name mappings.

除了一个非常好的Unicode存储模型外,它还有许多用于处理Unicode的API。 作为语言的一部分或出色的crates.io索引 。 这包括大小写折叠,分类,Unicode正则表达式,Unicode规范化,一致性良好的URI / IRI / URL API,分段或简单的名称映射。

What’s the downside? You can’t do "föo"[1] and expect 'ö' to come back. But that’s not a good idea anyways.

缺点是什么? 您不能执行“föo” [1]并期望'ö'回来。 但这并不是一个好主意。

As an example of how interaction with the OS works, here is an example application that opens a file in the current working directory and prints the contents and the filename:

作为与操作系统交互工作方式的一个示例,以下是一个示例应用程序,该应用程序在当前工作目录中打开一个文件并打印内容和文件名:

useuse    stdstd :::: envenv ;; 

useuse    stdstd :::: fsfs ;; 



fnfn    exampleexample ()()    ->->    ResultResult << (),(),    BoxBox << ErrorError >>>>    {{ 

         letlet    herehere    ==    trytry !! (( envenv :::: current_dircurrent_dir ());()); 

         printlnprintln !! (( "Contents in: {}""Contents in: {}" ,,    herehere .. displaydisplay ());()); 

         forfor    entryentry    inin    trytry !! (( fsfs :::: read_dirread_dir (( && herehere ))))    {{ 

                 letlet    pathpath    ==    trytry !! (( entryentry ).). pathpath ();(); 

                 letlet    mdmd    ==    trytry !! (( fsfs :::: metadatametadata (( && pathpath ));)); 

                 printlnprintln !! (( "  {} ({} bytes)""  {} ({} bytes)" ,,    pathpath .. displaydisplay (),(),    mdmd .. lenlen ());()); 

         }} 

         OkOk (())(()) 

}} 



fnfn    mainmain ()()    {{ 

         exampleexample ().(). unwrapunwrap ();(); 

}} 

All the IO operations use these Path objects that were also shown before, which encapsulate the operating system’s internal path properly. They might be bytes, unicode or whatever else the operating system uses but the can be formatted properly by calling .display() on them which return an object that can format itself into a string. This is convenient because it means you never accidentally leak out bad strings like we do in Python 3 for instance. There is a clear separation of concerns.

所有的IO操作都使用前面也显示过的这些Path对象,它们正确封装了操作系统的内部路径。 它们可以是字节,unicode或操作系统使用的其他任何东西,但是可以通过在它们上调用.display()来正确格式化它们,这将返回一个可以将自身格式化为字符串的对象。 这很方便,因为这意味着您绝不会像我们在Python 3中那样意外泄漏出错误的字符串。 存在明显的关注点分离。

发行和图书馆 (Distribution and Libraries)

Rust comes with a combination of virtualenv+pip+setuptools called “cargo”. Well, not entirely virtualenv as it can only work with one version of Rust by default, but other than that it works as you expect. Even better than in Python land can you depend on different versions of libraries and depend on git repositories or the crates.io index. If you get rust from the website it comes with the cargo command that does everything you would expect.

Rust附带了称为“ cargo”的virtualenv + pip + setuptools组合。 嗯,不完全是virtualenv,因为默认情况下它只能与一个版本的Rust一起使用,但除此之外,它可以按预期工作。 甚至比在Python领域中更好的情况下,您还可以依赖于不同版本的库并取决于git存储库或crates.io索引。 如果您从网站上生锈,它将附带有cargo命令,该命令可以完成您期望的一切。

Rust作为Python的替代品? (Rust as a Python Replacement?)

I don’t think there is a direct relationship between Python and Rust. Python shines in scientific computing for instance and I don’t think that this is something that can Rust tackle in the nearest future just because of how much work that would be. Likewise there really is no point in writing shell scripts in Rust when you can do that in Python. That being said, I think like many Python programmers started to pick up Go, even more will start to look at Rust for some areas where they previously used Python for.

我认为Python和Rust之间没有直接关系。 例如,Python在科学计算领域大放异彩,我不认为Rust可以在不久的将来解决这个问题,因为它会做很多工作。 同样,当您可以在Python中进行脚本编写时,在Rust中编写外壳脚本确实没有任何意义。 话虽这么说,我认为就像许多Python程序员开始学习Go一样,更多人会开始关注Rust以前使用Python的某些领域。

It’s a very powerful language, standing on strong foundations, under a very liberal license, with a very friendly community and driving by a democratic approach to language evolution.

这是一种非常强大的语言,它在非常自由的许可下立足于坚实的基础,拥有非常友好的社区,并通过民主的方式推动语言发展。

Because Rust requires very little runtime support it’s very easy to use via ctypes and CFFI with Python. I could very well envision a future where there is a Python package that would allow the distribution of a binary module written in Rust and callable from Python without any extra work from the developer needed.

由于Rust几乎不需要运行时支持,因此通过ctypes和CFFI与Python一起使用非常容易。 我可以很好地预见未来的发展,那里有一个Python软件包,该软件包将允许分发用Rust编写并可以从Python调用的二进制模块,而无需开发人员进行任何额外的工作。

翻译自: https://www.pybloggers.com/2015/05/rust-for-python-programmers/

rust python

你可能感兴趣的