LINUX.ORG.RU

Помогите разобраться с владением в Rust

 ,


0

3

В общем для интереса решил описать структурку с огикой управления Semantic Version. Вот код:

use std::fmt;
use std::cmp;
use std::result::Result::{self, Ok, Err};


struct Version {
    major: i64,
    minor: i64,
    patch: i64
}

impl Version {
    fn new() -> Version {
        Version{major: 0, minor: 0, patch: 0}
    }

    fn value(&self) -> String {
        format!("{}.{}.{}", self.major, self.minor, self.patch)
    }

    fn inc_major(&self) -> Version {
        Version{major: self.major + 1, minor: 0, patch: 0}
    }

    fn inc_minor(&self) -> Version {
        Version{major: self.major, minor: self.minor + 1, patch: 0}
    }

    fn inc_patch(&self) -> Version {
        Version{major: self.major, minor: self.minor, patch: self.patch + 1}
    }
}

struct VersionBuilder(Version);

impl VersionBuilder {
    fn new() -> VersionBuilder {
        VersionBuilder(Version::new())
    }

    fn major(mut self, value: i64) -> VersionBuilder {
        self.0.major = value;
        self
    }

    fn minor(mut self, value: i64) -> VersionBuilder {
        self.0.minor = value;
        self
    }

    fn patch(mut self, value: i64) -> VersionBuilder {
        self.0.patch = value;
        self
    }

    fn finalize(self) -> Version {
        self.0
    }
}

impl std::fmt::Display for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Version({})", self.value())
    }
}

impl cmp::PartialEq for Version {
    fn eq(&self, other: &Self) -> bool{
        if (self.major == other.major) &&
            (self.minor == other.minor) &&
            (self.patch == other.patch) {
                return true
            }

        false
    }
}

impl cmp::Eq for Version {}


impl cmp::PartialOrd for Version {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        match self.major.cmp(&other.major) {
            cmp::Ordering::Greater => return Some(cmp::Ordering::Greater),
            cmp::Ordering::Less    => return Some(cmp::Ordering::Less),
            cmp::Ordering::Equal   =>
                match self.minor.cmp(&other.minor) {
                    cmp::Ordering::Greater => return Some(cmp::Ordering::Greater),
                    cmp::Ordering::Less    => return Some(cmp::Ordering::Less),
                    cmp::Ordering::Equal   => return Some(self.patch.cmp(&other.patch))
            }
        }
    }
}

impl cmp::Ord for Version {
    fn cmp(&self, other: &Self) -> cmp::Ordering{
        self.partial_cmp(other).unwrap()
    }
}


pub struct ParseVersionError { _priv: () }

impl fmt::Display for ParseVersionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "предоставленная строка должна содержать три подверсии разделенные \
         точкой: `{major}.{minor}.{patch}`".fmt(f)
    }
}

fn check_version(v1: &Version, v2: &Version) {
    match v1.cmp(&v2) {
        cmp::Ordering::Greater => println!("{} больше {}", v1, v2),
        cmp::Ordering::Less    => println!("{} меньше {}", v1, v2),
        cmp::Ordering::Equal   => println!("Версии равны: {}", v1)
    }
}

fn main() {
    let v1 = &VersionBuilder::new()
        .major(2)
        .patch(3);

    v1.minor(11);

    v1.minor(2);

    v1.major(1);

    let v2 = &Version{major: 1, minor: 2, patch:5}.inc_minor();

    check_version(v1.finalize(), v2);
}

Сейчас при сборке он обсыпается на последней строчке, т.к. ожидает получить ссылку, а получает объект:

main.rs:161:19: 161:32 error: mismatched types:
 expected `&Version`,
    found `Version`
(expected &-ptr,
    found struct `Version`) [E0308]
main.rs:161     check_version(v1.finalize(), v2);

Если же я ставлю перед v1 «&» v1.finalize(), то получаю:

main.rs:153:5: 153:7 error: cannot move out of borrowed content
main.rs:153     v1.minor(11);
                ^~
main.rs:155:5: 155:7 error: cannot move out of borrowed content
main.rs:155     v1.minor(2);
                ^~
main.rs:157:5: 157:7 error: cannot move out of borrowed content
main.rs:157     v1.major(1);
                ^~
main.rs:161:20: 161:22 error: cannot move out of borrowed content
main.rs:161     check_version(&v1.finalize(), v2);

Я что-то запутался...

Как мне правильно разрулить здесь с ссылками и владением?

Может будут еще акие замечания?

Решение

★★★★★

Говнецо

Фууу, rast.....

anonymous ()

Во-первых.

let v1 = &VersionBuilder::new()
        .major(2)
        .patch(3);
VVV
let v1 = VersionBuilder::new()
        .major(2)
        .patch(3);
Что бы объект существовал, нужно что бы кто-то им владел. Ссылки не дают владение над объектом. В твоем варианте, ты пытаешься взять ссылку на объект, которым никто не владеет и время жизни которого закончилось.
let v2 = &Version{major: 1, minor: 2, patch:5}.inc_minor();
Вот тут то же самое.
Во-вторых:
fn minor(mut self, value: i64) -> VersionBuilder {
    self.0.minor = value;
    self
}
Зачем вообще так делать в расте? Почему не
fn minor(&mut self, value: i64) {
    self.0.minor = value;
}

Aswed ★★★★★ ()
Последнее исправление: Aswed (всего исправлений: 1)

Я плохо знаю Rust, но думаю, что дело в том, что в VersionBuilder у тебя не self ссылки, а просто self. Это значит, что self будет «поглощен» вызовом функции и не будет доступен после вызова.

anonymous ()
Ответ на: комментарий от Aswed
fn minor(mut self, value: i64) -> VersionBuilder {
    self.0.minor = value;
    self
}

Ну за тем что, я хочу сделать например так:

let v1 = VersionBuilder::new().major(1).minor(2).patch(3).finalize();

Остальное пока осознаю.

deterok ★★★★★ ()

Ты копируешь объект!

fn major(mut self, value: i64) -> VersionBuilder {
fn minor(mut self, value: i64) -> VersionBuilder {
fn patch(mut self, value: i64) -> VersionBuilder {

Либо используй ссылки.

fn major(&mut self, value: i64) -> &mut VersionBuilder {
fn minor(&mut self, value: i64) -> &mut VersionBuilder {
fn patch(&mut self, value: i64) -> &mut VersionBuilder {

Либо:

#[derive(Copy, Clone)]
struct VersionBuilder(Version);

Опять же, копирование:

fn finalize(self) -> Version {

Надо добавить:

#[derive(Copy, Clone)]
struct Version {

https://play.rust-lang.org/?gist=14bdf38b8f661ca7ec06&version=stable

anonymous ()
Ответ на: комментарий от Aswed

Ага, переписал вот так:

impl VersionBuilder {
    fn new() -> VersionBuilder {
        VersionBuilder(Version::new())
    }

    fn major(&mut self, value: i64) -> &mut VersionBuilder {
        self.0.major = value;
        self
    }

    fn minor(&mut self, value: i64) -> &mut VersionBuilder {
        self.0.minor = value;
        self
    }

    fn patch(&mut self, value: i64) -> &mut VersionBuilder {
        self.0.patch = value;
        self
    }

    fn finalize(&self) -> Version {
        Version{.. self.0}
    }
}

Но теперь хочу что бы вот такой код работал:

    let mut v1 = VersionBuilder::new().minor(4);

    v1.major(1).minor(2);
    v1.patch(3);

    let v2 = Version{major: 1, minor: 2, patch:5}.inc_minor();

    check_version(&v1.finalize(), &v2);

Но т.к. я возвращаю &mut Self, то объект пропадает еще до того как я успею его задереферить и присвоить чему-нибудь...

deterok ★★★★★ ()
Ответ на: комментарий от deterok

Если я правильно понимаю, как работает Rust, то у тебя так не получится сделать. Либо тебе нужно сразу:

let v = VersionBuilder::new().minor(4).major(1).finalize();
Либо:
let mut v1 = VersionBuilder::new();
v1.minor(4).major(1);
let v2 = v1.finalize();
Либо как-то так:
let mut v1 = VersionBuilder::new().minor(4).clone();
v1.major(1);
let v2 = v1.finalize();
Точно сказать не могу, тк язык знаю плохо.

anonymous ()

Всем спасибо. Разобрался!

deterok ★★★★★ ()
Ответ на: комментарий от deterok

В расте лучше делать так. Тот случай, когда пытаешься лезть в чужой монастырь со своим уставом.

let mut v1 = VersionBuilder::new();
v1.minor(4);

Aswed ★★★★★ ()
Ответ на: комментарий от Aswed

Зачем вообще так делать в расте? Почему не

Потому что разработчики тянут с такой полезной фичей как именованные аргументы и значения по умолчанию. В итоге этих дурацких «билдеров» везде полно.

DarkEld3r ★★★★★ ()
Ответ на: комментарий от deterok

Окончательный исправленный результат исходного сырца, выложи пожалуйста ниже, чтобы можно было посмотреть что в итоге получилось.

foror ★★★★ ()
Ответ на: комментарий от deterok

Опубликуй свое решение, чтобы другие могли посмотреть; возможно, и нагуглить.

Virtuos86 ★★★★★ ()
Ответ на: комментарий от Aswed

В расте лучше делать так.

Источник? На мой взгляд, это совсем не rust-way.

ovk48 ★★★ ()

Решение

use std::fmt;
use std::cmp;
use std::result::Result::{self, Ok, Err};


#[derive(Eq)]
struct Version {
    major: i64,
    minor: i64,
    patch: i64
}

impl Version {
    fn new() -> Version {
        Version{major: 0, minor: 0, patch: 0}
    }

    fn value(&self) -> String {
        format!("{}.{}.{}", self.major, self.minor, self.patch)
    }

    fn inc_major(&self) -> Version {
        Version{major: self.major + 1, minor: 0, patch: 0}
    }

    fn inc_minor(&self) -> Version {
        Version{major: self.major, minor: self.minor + 1, patch: 0}
    }

    fn inc_patch(&self) -> Version {
        Version{major: self.major, minor: self.minor, patch: self.patch + 1}
    }
}

struct VersionBuilder(Version);


impl VersionBuilder {
    fn new() -> VersionBuilder {
        VersionBuilder(Version::new())
    }

    fn major(&mut self, value: i64) -> &mut VersionBuilder {
        self.0.major = value;
        self
    }

    fn minor(&mut self, value: i64) -> &mut VersionBuilder {
        self.0.minor = value;
        self
    }

    fn patch(&mut self, value: i64) -> &mut VersionBuilder {
        self.0.patch = value;
        self
    }

    fn finalize(&self) -> Version {
        Version{.. self.0}
    }
}

impl std::fmt::Display for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Version({})", self.value())
    }
}


impl cmp::PartialEq for Version {
    fn eq(&self, other: &Self) -> bool{
        if (self.major == other.major) &&
            (self.minor == other.minor) &&
            (self.patch == other.patch) {
                return true
            }

        false
    }
}

impl cmp::PartialOrd for Version {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        match self.major.cmp(&other.major) {
            cmp::Ordering::Greater => return Some(cmp::Ordering::Greater),
            cmp::Ordering::Less    => return Some(cmp::Ordering::Less),
            cmp::Ordering::Equal   =>
                match self.minor.cmp(&other.minor) {
                    cmp::Ordering::Greater => return Some(cmp::Ordering::Greater),
                    cmp::Ordering::Less    => return Some(cmp::Ordering::Less),
                    cmp::Ordering::Equal   => return Some(self.patch.cmp(&other.patch))
            }
        }
    }
}

impl cmp::Ord for Version {
    fn cmp(&self, other: &Self) -> cmp::Ordering{
        self.partial_cmp(other).unwrap()
    }
}

impl std::str::FromStr for Version {
    type Err = ParseVersionError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {

        let splitted: Vec<&str> = s.split('.').collect();
        let count = splitted.len();


        if count != 3 {
            return Err(ParseVersionError{_priv: ()})
        }

        let major = splitted[0].parse::<i64>().unwrap();
        let minor = splitted[1].parse::<i64>().unwrap();
        let patch = splitted[2].parse::<i64>().unwrap();

        let v = Version{
            major: major,
            minor: minor,
            patch: patch};

        Ok(v)
    }
}

struct ParseVersionError { _priv: () }

impl std::fmt::Display for ParseVersionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "provided string must contain three subversions separated by a dot.\n\
         For example: \"1.2.3\".".fmt(f)
    }
}

fn check_version(v1: &Version, v2: &Version) {
    match v1.cmp(v2) {
        cmp::Ordering::Greater => println!("{} больше {}", v1, v2),
        cmp::Ordering::Less    => println!("{} меньше {}", v1, v2),
        cmp::Ordering::Equal   => println!("Версии равны: {}", v1)
    }
}

fn main() {
    let mut v1 = VersionBuilder::new();

    v1.major(1).minor(2);
    v1.patch(3);

    let v2 = Version{major: 1, minor: 2, patch:5}.inc_minor();

    check_version(&v1.finalize(), &v2);

    // match "3.4..ы.5".parse::<Version>() { // Проверка ошибки
    match "3.4.5".parse::<Version>() {
        Ok(v)    => println!("{}", v),
        Err(err) => println!("Error: {}", err)
    }
}
deterok ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.