【问题标题】:How to set correctly a generic collection type in rust如何在 rust 中正确设置通用集合类型
【发布时间】:2019-12-20 23:14:05
【问题描述】:

我正在尝试在 FreeBSD 12 下测试 Rust 1.39 中的一些语言特性,并与 Free Pascal 3.0.4 进行比较,以获得一个简单的通用点集合,其中包含由字符串键寻址的 2D 点。不幸的是,泛型类型声明的代码不会在很早的状态下编译并停止:

error[E0106]: missing lifetime specifier
  --> src/main.rs:11:31
   |
11 |     type TPointMap = BTreeMap<&TString, TPoint>;
   |     

如何重写 Rust 代码?

详情:

为了测试语言行为,我用 Rust 和 Pascal 编写了两个小程序,“在语法上”处理相同的上下文。 Pascal 程序是一个简单的声明:

  1. 一些普通类型,一个记录类型和一个通用地图容器,
  2. 之后将使用它来定义一个点,将该点存储在一个新分配的地图中,
  3. 通过key搜索点并将数据写入STDIO
  4. 最后释放地图。
program test;
uses fgl; { Use the free pascal generics library }
type
   TDouble   = Double; { Define a 64 bit float } 
   TString   = String; { Define a string type  } 
   TPoint    = record  { Define a point record }
                  X : TDouble; { Coordinate X }
                  Y : TDouble; { Coordinate Y }
               end; 
   { Define a map of points with strings as key }
   TPointMap = specialize TFPGMap<TString, TPoint>;

{ Test program } 
var
   map   : TPointMap; { Declare the map variable }
   point : TPoint;    { Declare a point variable }
   found : TPoint;    { Varaiable for a found point }
   key   : TString;   { Variable to address the point in the map } 
begin
   map := TPointMap.create; { Allocate a new ma container }
   with point do begin      { Set the point variable }
      x := 1.0; y := 2.0;
   end;
   key := '123';              { Set the key address to '123'  }
   map.add(key,point);        { Store the point in the map }

   { Search the point an write the result in the rusty way }
   case map.TryGetData(key, found)  of
     true  : writeln('X: ',found.X:2;, ' Y:', found.Y:2:2);
     false : writeln('Key ''',key,''' not found');
   end;
   map.free;  { De-allocate the map }
   { Plain types are de-allocated by scope }
end.   

程序编译并给我:

$ ./main 
X: 1.00 Y:2.00

这是我错误的 Rust 版本的代码:

use std::collections::BTreeMap; // Use a map from the collection 

type TDouble = f64; // Define the 64 bit float type
type TString = str; // Define the string type
struct TPoint {     // Define the string type
    x: TDouble,     // Coordinate X
    y: TDouble,     // Coordinate Y
}

// Define a map of points with strings as key 
type TPointMap = BTreeMap<&TString, TPoint>;

// Test program
fn main() {
    let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
    let mut map = TPointMap::new();        // Declare the map and allocate it
    let key: TString = "123";              // Declare and define the address of point 
    map.insert(&key, point);               // Add the point to the map 
    // search the point and print it
    match map.get(&key) {
        Some(found) => println!("X: {} Y: {}", found.X, found.y),
        None => println!("Key '{}' not found", key),
    }
   // map is de-allocated by scope
}

备注:由于借用和所有权的概念,我知道某些代码行不能在 Rust 代码中使用。线

 match map.get(&key)...

就是其中之一。

【问题讨论】:

标签: generics rust


【解决方案1】:

要大致相当于freepascal 版本,TString 可能应该是String 而不是strfreepascal 字符串是 (depending on some flags) 一个指针、一个长度和一个堆分配的字符数组。这(几乎)正是String 的含义。 str 只是字符数组,没有大小,所以它总是必须在某种(胖)指针后面。

一旦进行了更改,就只有少数其他事情可以修复代码。 TPointMap 需要生命周期参数,因为它使用引用类型。引用的生命周期必须来自某个地方,因此我们将 TPointMap 设为该生命周期的泛型。

type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;

如果您的用例允许,您也可以考虑简单地使用BTreeMap&lt;TString, TPoint&gt;

我们需要做一些转换来声明key: TString。字符串文字的类型为'static str,但有一个简单的to_string 方法可以将它们转换为Strings。

let key: TString = "123".to_string(); 

最后,found.X 有错别字。

Some(found) => println!("X: {} Y: {}", found.x, found.y),

我们有

use std::collections::BTreeMap; // Use a map from the collection

type TDouble = f64; // Define the 64 bit float type
type TString = String; // Define the string type
struct TPoint {
    // Define the string type
    x: TDouble, // Coordinate X
    y: TDouble, // Coordinate Y
}

// Define a map of points with strings as key
type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;

// Test program
fn main() {
    let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
    let mut map = TPointMap::new(); // Declare the map and allocate it
    let key: TString = "123".to_string(); // Declare and define the address of point
    map.insert(&key, point); // Add the point to the map
                             // search the point and print it
    match map.get(&key) {
        Some(found) => println!("X: {} Y: {}", found.x, found.y),
        None => println!("Key '{}' not found", key),
    }
    // map is de-allocated by scope
}

(playground)

另见What are the differences between Rust's String and str?

【讨论】:

  • 游乐场是 rust 基础设施的一个非常好的功能。
  • FreePascal/Delphi 字符串是一种自动(引用计数)类型,附加了长度和编码。
【解决方案2】:

您看到此失败的原因是您需要一个生命周期的参考。 Rust 目前无法知道您的引用应该持续多长时间。

一旦你解决了这个问题,你就会遇到这样一个事实:你不能创建类型为 str 的变量,因为它没有大小,而且编译器无法在编译时告诉你要分配多少空间。

您可以在此处进行的最简单、最小的更改是更改以下几行:

type TString = &'static str;

type TPointMap = BTreeMap<TString, TPoint>;

(您还需要将x 中的x 小写,因为Rust 区分大小写。)

这将告诉编译器您的字符串类型是&amp;'static str,这是字符串文字的类型,因为它们与程序一样长。

您还可以通过执行以下操作使其与任意生命周期的 str 引用一起使用:

type TString<'a> = &'a str;

type TPointMap<'a> = BTreeMap<TString<'a>, TPoint>;

但是,在许多程序中,您可能希望插入不限于字符串文字的字符串,并避免在映射的生命周期内进行必要的借用,例如,如果您想从函数返回映射。在这种情况下,使用拥有的String 对象可能是有意义的,这样该对象的生命周期与地图一样长。在这种情况下,您的代码将如下所示:

use std::collections::BTreeMap; // Use a map from the collection

type TDouble = f64; // Define the 64 bit float type
type TString = String; // Define the string type
struct TPoint {     // Define the string type
    x: TDouble,     // Coordinate X
    y: TDouble,     // Coordinate Y
}

// Define a map of points with strings as key
type TPointMap = BTreeMap<TString, TPoint>;

// Test program
fn main() {
    let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
    let mut map = TPointMap::new();        // Declare the map and allocate it
    let key: TString = "123".to_string();  // Declare and define the address of point
    map.insert(key.clone(), point);        // Add the point to the map
    // search the point and print it
    match map.get(&key) {
        Some(found) => println!("X: {} Y: {}", found.x, found.y),
        None => println!("Key '{}' not found", key),
    }
   // map is de-allocated by scope
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-12
    • 2015-05-19
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 2021-10-21
    相关资源
    最近更新 更多