如何在 Rust 中正确设置泛型集合类型

huc*_*inn 2 generics rust

我正在尝试在 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>;
   |     
Run Code Online (Sandbox Code Playgroud)

我该如何重写 Rust 代码?

细节:

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

  1. 一些普通类型、记录类型和通用地图容器,
  2. 这将被用来定义一个点,将这个点存储在一个新分配的地图中,
  3. 通过键搜索点并将数据写入 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.   
Run Code Online (Sandbox Code Playgroud)

该程序编译并给我:

$ ./main 
X: 1.00 Y:2.00
Run Code Online (Sandbox Code Playgroud)

这是我不正确的 Rust 版本代码:

$ ./main 
X: 1.00 Y:2.00
Run Code Online (Sandbox Code Playgroud)

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

 match map.get(&key)...
Run Code Online (Sandbox Code Playgroud)

是其中之一。

SCa*_*lla 5

大致相当于freepascal版本,TString应该是String而不是str. 甲freepascal字符串是(依赖于一些标志)的指针,一个长度和字符的堆分配阵列。这(几乎)正是如此Stringstr只是字符数组并且未调整大小,因此它始终必须位于某种(胖)指针之后。

一旦进行了更改,只需要做一些其他事情来修复代码。TPointMap需要一个生命周期参数,因为它使用引用类型。引用的生命周期必须来自某个地方,所以我们TPointMap在那个生命周期中使用泛型。

type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;
Run Code Online (Sandbox Code Playgroud)

BTreeMap<TString, TPoint>如果您的用例允许,您也可以考虑简单地使用。

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

let key: TString = "123".to_string(); 
Run Code Online (Sandbox Code Playgroud)

最后,有一个错字found.X

Some(found) => println!("X: {} Y: {}", found.x, found.y),
Run Code Online (Sandbox Code Playgroud)

总之,我们有

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
}
Run Code Online (Sandbox Code Playgroud)

(操场)

另请参阅RustStringstr?之间的区别是什么?