TcpStream::connect – match arms have incompatible type

huangapple go评论63阅读模式
英文:

TcpStream::connect - match arms have incompatible type

问题

以下是您要的翻译部分:

我正在尝试在Rust中编写基本的网络编程代码,但遇到了一个我不理解的错误。到目前为止,我一直在Rust中使用match语句来检查错误,但当我尝试检查TcpStream::connect()时,出现了一个意外的错误:

我的代码:
```rust
use std::net::TcpStream;

fn main() {
    let mut server = match TcpStream::connect("127.0.0.1:23456"){
        Ok(x) => x,
        Err(x) => println!("无法连接到服务器: {x}"),
    };
}

编译器错误:

error[E0308]: 'match' 分支具有不兼容的类型
 --> src/main.rs:8:19
  |
6 |       let mut server = match TcpStream::connect("127.0.0.1:23456"){
  |  ______________________-
7 | |         Ok(x) => x,
  | |                  - 这被确定为类型 `TcpStream`
8 | |         Err(x) => println!("无法连接到服务器: {x}"),
  | |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
                        预期结构 `TcpStream`,找到 `()`
9 | |     };
  | |_____- 'match' 分支具有不兼容的类型
  |

我每次使用match语句时,都允许我将Result类型解构为OK情况下的返回值(如上所示),或者在错误情况下返回错误字符串。

TcpStream::connect()确实返回TcpStream,但为什么编译器坚持要求错误情况也返回TcpStream?


<details>
<summary>英文:</summary>

I&#39;m trying to write basic networking code in Rust, but running into an error I don&#39;t understand. I have been using match statements to error check everything in Rust so far, but when I try to error check TcpStream::connect(), I get an unexpected error:

My code:

use std::net::TcpStream;

fn main() {
let mut server = match TcpStream::connect("127.0.0.1:23456"){
Ok(x) => x,
Err(x) => println!("Could not connect to server: {x}"),
};
}


The compiler error:

error[E0308]: match arms have incompatible types
--> src/main.rs:8:19
|
6 | let mut server = match TcpStream::connect("127.0.0.1:23456"){
| ______________________-
7 | | Ok(x) => x,
| | - this is found to be of type TcpStream
8 | | Err(x) => println!("Could not connect to server: {x}"),
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
expected struct TcpStream, found ()
9 | | };
| |_____- match arms have incompatible types
|


Every other time I use a match statement it allows me to destructure the Result type into a return value in the OK case (as above), or an error string in the error case.

It is the case that TcpStream::connect() returns a TcpStream, but why is the compiler insisting that the error case also needs to return a TcpStream?

</details>


# 答案1
**得分**: 2

The value of the `match` statement gets assigned to `server`.

However, both branches of your `match` statement return a different type.

- `Ok(x)` returns `x`, which is of type `TcpStream`.
- `Err(x)` returns the result of `println!()`, which has the return value `()`.

`TcpStream` and `()` are incompatible.

Just think about the code after the match statement. What should the `server` variable be? You don't stop the execution when the error happens, you simply `println!()` and continue. So **something** has to be written to the `server` variable.

If you `panic!()` instead of `println!()`, meaning print and abort, then it compiles because it knows that the `Err` case won't continue afterwards:

```rust
use std::net::TcpStream;

fn main() {
    let mut server = match TcpStream::connect("127.0.0.1:23456") {
        Ok(x) => x,
        Err(x) => panic!("Could not connect to server: {x}"),
    };
}
thread 'main' panicked at 'Could not connect to server: Connection refused (os error 111)', src/main.rs:6:19

That said, if this is your desired behavior, there is a short form for it:

use std::net::TcpStream;

fn main() {
    let mut server = TcpStream::connect("127.0.0.1:23456").expect("Could not connect to server");
}
thread 'main' panicked at 'Could not connect to server: Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }', src/main.rs:4:60

I recommend handling the error properly, though.

There are many ways to do that, so this part will be opinionated.

I personally like miette (an alternative would be anyhow):

use miette::{Context, IntoDiagnostic};
use std::net::TcpStream;

fn main() -> miette::Result<()> {
    let mut _server = TcpStream::connect("127.0.0.1:23456")
        .into_diagnostic()
        .wrap_err("Could not connect to server.")?;

    // Do something with server

    Ok(())
}
Error: 
  × Could not connect to server.
  ╰─▶ Connection refused (os error 111)
英文:

The value of the match statement gets assigned to server.

However, both branches of your match statement return a different type.

  • Ok(x) returns x, which is of type TcpStream.
  • Err(x) returns the result of println!(), which has the return value ().

TcpStream and () are incompatible.

Just think about the code after the match statement. What should the server variable be? You don't stop the execution when the error happens, you simply println!() and continue. So something has to be written to the server variable.

If you panic!() instead of println!(), meaning print and abort, then it compiles because it knows that the Err case won't continue afterwards:

use std::net::TcpStream;

fn main() {
    let mut server = match TcpStream::connect(&quot;127.0.0.1:23456&quot;) {
        Ok(x) =&gt; x,
        Err(x) =&gt; panic!(&quot;Could not connect to server: {x}&quot;),
    };
}
thread &#39;main&#39; panicked at &#39;Could not connect to server: Connection refused (os error 111)&#39;, src/main.rs:6:19

That said, if this is your desired behavior, there is a short form for it:

use std::net::TcpStream;

fn main() {
    let mut server = TcpStream::connect(&quot;127.0.0.1:23456&quot;).expect(&quot;Could not connect to server&quot;);
}
thread &#39;main&#39; panicked at &#39;Could not connect to server: Os { code: 111, kind: ConnectionRefused, message: &quot;Connection refused&quot; }&#39;, src/main.rs:4:60

I recommend handling the error properly, though.

There are many ways to do that, so this part will be opinionated.

I personally like miette (an alternative would be anyhow):

use miette::{Context, IntoDiagnostic};
use std::net::TcpStream;

fn main() -&gt; miette::Result&lt;()&gt; {
    let mut _server = TcpStream::connect(&quot;127.0.0.1:23456&quot;)
        .into_diagnostic()
        .wrap_err(&quot;Could not connect to server.&quot;)?;

    // Do something with server

    Ok(())
}
Error: 
  &#215; Could not connect to server.
  ╰─▶ Connection refused (os error 111)

答案2

得分: 2

Your server variable can't be both TcpStream and the unit type ()

尝试传播错误而不是这样。

这里有来自官方文档的示例:

use std::io::prelude::*;
use std::net::TcpStream;

fn main() -> std::io::Result<()> {
    let mut stream = TcpStream::connect("127.0.0.1:34254")?;

    stream.write(&[1])?;
    stream.read(&mut [0; 128])?;
    Ok(())
}

如果它无法连接到服务器,它会发出错误消息

Error: Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }

并设置一个非零的退出码

$ echo $?
1
英文:

Your server variable can't be both TcpStream and the unit type ()

Try propagating the error instead.

Here is an example from the official documentation:

use std::io::prelude::*;
use std::net::TcpStream;

fn main() -&gt; std::io::Result&lt;()&gt; {
    let mut stream = TcpStream::connect(&quot;127.0.0.1:34254&quot;)?;

    stream.write(&amp;[1])?;
    stream.read(&amp;mut [0; 128])?;
    Ok(())
}

If it can't connect to the server, it emits an error message

Error: Os { code: 111, kind: ConnectionRefused, message: &quot;Connection refused&quot; }

And sets a non-zero exit code

$ echo $?
1

huangapple
  • 本文由 发表于 2023年2月6日 02:17:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/75354488.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定