psyhon 사용자가 읽는 프로그래밍 Rust 제2장 전반부
개요
Rust프로그래밍 Rust를 배우고 싶다는 것을 사서 코드를 베껴 쓰면서 자신이 익숙해진python의 실행 결과를 비교하면서 이해했기 때문이다.
코드 여기 있어요.
Toy 프로그램
두 정수의 최대 공약수를 계산하는 프로그램을 써 보아라.
fn gcd(mut n: u64, mut m: u64) -> u64{
assert!(n !=0 && m !=0);
while m !=0 {
if m < n{
let t = m;
m = n;
n = t;
}
m = m % n;
}
n
}
mut에서 가변을 나타낸다.수치의 유형은 다음과 같습니다.유형 이름
의향
i32
기호가 있는 32비트 정수
u64
부호 없는 64비트 정수
u8
기호 없는 8비트 정수
f32
단정밀도 부동 소수점
f64
더블 정밀도 부동 소수점
「!」첨부된 것은 매크로 호출입니다.assert!매개변수가 진짜인지 확인합니다.그렇지 않으면 프로그램이 갑자기 끝나도록 메시지를 보낼 것이다.이런 끝을 공황이라고 부른다.
while 문장에서 ()로 조건식을 포위할 필요가 없습니다.제어 대상 문장이 {}에 포위되었습니다.
파이썬으로 쓰면 이런 느낌이잖아요.
def gcd(n: int, m: int) -> int:
assert (n != 0) & (m != 0), "n and m must be non-zero"
while m != 0:
if m < n :
t = m
m = n
n = t
m = m % n
return n
단원 테스트
Rust에서 테스트 기구는 언어에 편입되어 코드에 첨부된 형식으로 이루어진다.
#[test]
fn test_gcd() {
assert_eq!(gcd(14, 15), 1);
assert_eq!(gcd(2 * 3 * 5 * 11 * 17, 3 * 7 * 11 * 13 * 19), 3 * 11);
assert_ne!(gcd(10,5,4))
}
이전 프로그램과 같은 파일의 끝에 쓰면 됩니다.Cargo test
를 실행하면 다음과 같은 결과를 얻을 수 있습니다.cargo test
Finished test [unoptimized + debuginfo] target(s) in 0.04s
Running unittests (target/debug/deps/rust-67ad38ae0a14e5b1)
running 1 test
test test_gcd ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
ptyhon에서 하면 끼워 넣은 unittest를 사용하거나 ptyest를 사용합니다.나는 기본적으로pytest를 사용하기 때문에pytest로 테스트를 써 보았다.
from python.main import gcd
import pytest
@pytest.mark.parametrize(
"x, y, answer", [
(10, 5, 5),
(36, 4, 4)
]
)
def test_gcd(x, y, answer):
assert gcd(x, y) == answer, f"gcd(10,5) should be {answer}"
@pytest.mark.parametrize(
"x, y, answer", [
(10, 5, 4),
(36, 4, 9)
]
)
def test_gcd_error(x, y, answer):
with pytest.raises(AssertionError):
assert gcd(x, y) == answer, f"gcd(10,5) should be {answer}"
명령행 매개변수
프로그램에 명령행 파라미터를 제공하는 방법에 대해 다음과 같은 프로그램을 시도해 보세요.
use std::str::FromStr;
use std::env;
fn main(){
let mut numbers = Vec::new();
for arg in env::args().skip(1) {
numbers.push(u64::from_str(&arg)
.expect("error parsing argument"));
}
if numbers.len() == 0 {
println!("Usage : gcd NUMBER ...");
std::process::exit(1);
}
let mut d = numbers[0];
for m in &numbers[1..] {
d = gcd(d, *m);
}
println!("The greatest common divisor of {:?} is {}", numbers, d);
}
main.rs를 이렇게 고쳐 썼어요.use 선언에서 표준 라이브러리의 TRAIT
FromStr
를 역할 영역으로 가져옵니다.TRAIT란 유형을 실현할 수 있는 방법의 집합을 말한다.FromStr TRAIT가 설치된 유형 모두from_str
메서드는 문자열을 분석하고 해당 유형의 값으로 변환하는 메서드입니다.FromStr
자체는 이 프로그램에 없지만 이 추적 방법from_str
으로 사용하기 위해 필요하다.두 번째use 성명에는 std:env 모듈이 포함되어 있습니다.이 프로그램에서 env 모듈에 있는args 함수를 사용하여 명령행 인자를 가져옵니다.
let mut numbers = Vec::new();
에서 가변적인 로컬 변수number
를 설명하고 빈 벡터로 초기화합니다.Vec는 크기가 변할 수 있는 벡터형으로python이 말한list에 해당한다.그러나mut를 넣지 않으면 동적 변화가 불가능하다.실제로numbers의 유형은
u64
즉Vec<u64>
이지만 후급pushu64
에 있기 때문에 유형 추론을 할 수 있기 때문에 여기에 명확하게 쓸 필요가 없다.for arg in env::args().skip(1) {
명령행 매개 변수를 처리합니다.env::args
균형기를 되돌려주기 때문에 for 순환에서 매개 변수를 처리할 수 있습니다.균형기의skip 방법은 주어진 수치에 색인할 필요가 없는 균형기를 생성합니다.따라서 첫 번째 값을 생략한 균형기가 생겼다.numbers.push(u64::from_str(&arg)
.expect("error parsing argument"));
이 호출u64::from_str
을 통해arg에 저장된 명령줄 파라미터를 64비트 정수로 제거합니다.from_str
가 되돌아오는 값은 u64 자체가 아니라 퍼스의 성공 여부를 지시하는 것이다Result
.Result
값은 다음 두 값을 취한다.Result
Rust는 예외 처리가 없기 때문에 오류는 Result 또는 공황 처리로 처리됩니다.퍼스가 성공했는지 확인하기 위해서 Result의 expect 방법을 사용합니다.Err(e)인 경우 expect에서 e에 대한 설명을 내보내고 프로그램 실행을 중단합니다.Ok(v)인 경우 v가 반환됩니다.
if numbers.len() == 0 {
println!("Usage : gcd NUMBER ...");
std::process::exit(1);
}
벡터에 최소한의 요소가 필요하기 때문에 이 검사를 진행한다.eprint!표준 오류 출력에 매크로를 사용하여 오류 메시지를 씁니다.let mut d = numbers[0];
for m in &numbers[1..] {
d = gcd(d, *m);
}
여기서 주의해야 할 것은 &numbers의 & 연산자와 *m의 * 연산자입니다.이전의 코드는 간단한 값 하나만 조작해서 정수와 같은 메모리에 고정된 블록에 수용할 수 있었지만 벡터의 크기는 임의이며 매우 큰 사이즈일 수도 있다.
이러한 값을 조작하려면 벡터의 소유권은numbers가 가지고 있지만 순환에서 그 요소만 빌려 썼다는 것을 알려야 한다.
&numbers[1..]
에서는 벡터의 두 번째 또는 그 이상의 요소를 대여한 참조를 나타냅니다.*m는 m 해결을 참고하는 연산자로서 참고 값을 되돌려줍니다.println!("The greatest common divisor of {:?} is {}", numbers, d);
마지막으로 결과를 표준 출력에 기록합니다.실제 실행 후
cargo run 46 23
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/rust 46 23`
The greatest common divisor of [46, 23] is 23
위에 차용이 있다는 말이 있어서 차용하지 않고 조작해 봤습니다.즉
&numbers[1..]
의'&'를 없애고 실행해 보자는 것이다.cargo run 46 23
Compiling rust v0.1.0 (/Users/ippei/workspace/programming_rust/chapter2/2.4/rust)
error[E0277]: the size for values of type `[u64]` cannot be known at compilation time
--> src/main.rs:16:14
|
16 | for m in numbers[1..] {
| ^^^^^^^^^^^^ expected an implementor of trait `IntoIterator`
|
= note: the trait bound `[u64]: IntoIterator` is not satisfied
= note: required because of the requirements on the impl of `IntoIterator` for `[u64]`
help: consider borrowing here
|
16 | for m in &numbers[1..] {
| +
16 | for m in &mut numbers[1..] {
| ++++
error[E0277]: `[u64]` is not an iterator
--> src/main.rs:16:14
|
16 | for m in numbers[1..] {
| ^^^^^^^^^^^^ expected an implementor of trait `IntoIterator`
|
= note: the trait bound `[u64]: IntoIterator` is not satisfied
= note: required because of the requirements on the impl of `IntoIterator` for `[u64]`
help: consider borrowing here
|
16 | for m in &numbers[1..] {
| +
16 | for m in &mut numbers[1..] {
| ++++
For more information about this error, try `rustc --explain E0277`.
error: could not compile `rust` due to 2 previous errors
결과는 이렇다.확실히 벡터가 일정하지 않아서 컴파일 시간을 모르는 오류가 발생했습니다.그리고 [u64] is not an iterator
의 오류가 발생했습니다.나는 U64가 하나의 균형기가 아니라고 생각한다. 그러나 빌려쓰지 않으면 Vec는 u64로 참조될 수 있습니까?제4장 이후에 차용에 대한 설명이 있으면 나중에 양보한다.
그리고 또 하나의 흥미로운 것은'&'를 더해서 좀 더 빌려 쓰는 게 어때요?이것은 컴파일러의 도움말입니다.코드를 쓰는 쪽에서 고맙습니다.
이어서 *m의 *를 없애고 해결을 참고하지 않고 실행해 봅시다.
cargo run 46 23
Compiling rust v0.1.0 (/Users/ippei/workspace/programming_rust/chapter2/2.4/rust)
error[E0308]: mismatched types
--> src/main.rs:17:20
|
17 | d = gcd(d, m);
| ^ expected `u64`, found `&u64`
|
help: consider dereferencing the borrow
|
17 | d = gcd(d, *m);
| +
For more information about this error, try `rustc --explain E0308`.
error: could not compile `rust` due to previous error
이런 상황에서 참고 해결이 없기 때문에 참고 자체가 전달되고 오류는 함수gcd
에 전달되는 값의 유형에 적합하지 않다고 여겨진다.여기도 참고 해결에 도움이 된다.
python으로 여기에 재현하면 다음과 같다.
import sys
def gcd(n: int, m: int) -> int:
assert (n != 0) & (m != 0), "n and m must be non-zero"
while m != 0:
if m < n :
t = m
m = n
n = t
m = m % n
return n
def main():
numbers = sys.argv
if len(numbers) == 1:
raise Exception("No arguments")
d = numbers[1]
for m in numbers[2:]:
d = gcd(int(d), int(m))
print(f"The greatest common divisor of {numbers[1:]} is {d}")
if __name__ == '__main__':
main()
python에서 명령행 파라미터를 개인적으로 제공하는 스크립트typer나 fire 등을 만듭니다.
Reference
이 문제에 관하여(psyhon 사용자가 읽는 프로그래밍 Rust 제2장 전반부), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/iusami/articles/prog_rust_chapter2_1sthalf텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)